From 8cdfa445d6629ffef4cb84967ff7017654045bc2 Mon Sep 17 00:00:00 2001 From: mo khan Date: Wed, 2 Jul 2025 18:36:06 -0600 Subject: chore: add vendor directory --- .../rustix/src/backend/linux_raw/arch/aarch64.rs | 269 +++ vendor/rustix/src/backend/linux_raw/arch/arm.rs | 266 +++ vendor/rustix/src/backend/linux_raw/arch/mips.rs | 544 +++++++ .../rustix/src/backend/linux_raw/arch/mips32r6.rs | 544 +++++++ vendor/rustix/src/backend/linux_raw/arch/mips64.rs | 467 ++++++ .../rustix/src/backend/linux_raw/arch/mips64r6.rs | 471 ++++++ vendor/rustix/src/backend/linux_raw/arch/mod.rs | 321 ++++ .../rustix/src/backend/linux_raw/arch/powerpc.rs | 414 +++++ .../rustix/src/backend/linux_raw/arch/powerpc64.rs | 414 +++++ .../rustix/src/backend/linux_raw/arch/riscv64.rs | 266 +++ vendor/rustix/src/backend/linux_raw/arch/s390x.rs | 288 ++++ vendor/rustix/src/backend/linux_raw/arch/thumb.rs | 323 ++++ vendor/rustix/src/backend/linux_raw/arch/x86.rs | 492 ++++++ vendor/rustix/src/backend/linux_raw/arch/x86_64.rs | 294 ++++ vendor/rustix/src/backend/linux_raw/c.rs | 385 +++++ vendor/rustix/src/backend/linux_raw/conv.rs | 1021 ++++++++++++ vendor/rustix/src/backend/linux_raw/event/epoll.rs | 74 + vendor/rustix/src/backend/linux_raw/event/mod.rs | 4 + .../rustix/src/backend/linux_raw/event/poll_fd.rs | 98 ++ .../rustix/src/backend/linux_raw/event/syscalls.rs | 358 ++++ vendor/rustix/src/backend/linux_raw/event/types.rs | 21 + vendor/rustix/src/backend/linux_raw/fs/dir.rs | 373 +++++ vendor/rustix/src/backend/linux_raw/fs/inotify.rs | 124 ++ vendor/rustix/src/backend/linux_raw/fs/makedev.rs | 19 + vendor/rustix/src/backend/linux_raw/fs/mod.rs | 13 + vendor/rustix/src/backend/linux_raw/fs/syscalls.rs | 1722 ++++++++++++++++++++ vendor/rustix/src/backend/linux_raw/fs/types.rs | 848 ++++++++++ vendor/rustix/src/backend/linux_raw/io/errno.rs | 552 +++++++ vendor/rustix/src/backend/linux_raw/io/mod.rs | 3 + vendor/rustix/src/backend/linux_raw/io/syscalls.rs | 364 +++++ vendor/rustix/src/backend/linux_raw/io/types.rs | 57 + .../rustix/src/backend/linux_raw/io_uring/mod.rs | 1 + .../src/backend/linux_raw/io_uring/syscalls.rs | 79 + vendor/rustix/src/backend/linux_raw/mm/mod.rs | 2 + vendor/rustix/src/backend/linux_raw/mm/syscalls.rs | 239 +++ vendor/rustix/src/backend/linux_raw/mm/types.rs | 297 ++++ vendor/rustix/src/backend/linux_raw/mod.rs | 112 ++ vendor/rustix/src/backend/linux_raw/mount/mod.rs | 2 + .../rustix/src/backend/linux_raw/mount/syscalls.rs | 237 +++ vendor/rustix/src/backend/linux_raw/mount/types.rs | 335 ++++ vendor/rustix/src/backend/linux_raw/net/addr.rs | 300 ++++ vendor/rustix/src/backend/linux_raw/net/mod.rs | 8 + vendor/rustix/src/backend/linux_raw/net/msghdr.rs | 125 ++ .../rustix/src/backend/linux_raw/net/netdevice.rs | 70 + .../src/backend/linux_raw/net/read_sockaddr.rs | 155 ++ .../rustix/src/backend/linux_raw/net/send_recv.rs | 87 + vendor/rustix/src/backend/linux_raw/net/sockopt.rs | 1099 +++++++++++++ .../rustix/src/backend/linux_raw/net/syscalls.rs | 731 +++++++++ .../src/backend/linux_raw/net/write_sockaddr.rs | 31 + vendor/rustix/src/backend/linux_raw/param/auxv.rs | 583 +++++++ vendor/rustix/src/backend/linux_raw/param/init.rs | 175 ++ .../src/backend/linux_raw/param/libc_auxv.rs | 198 +++ vendor/rustix/src/backend/linux_raw/param/mod.rs | 15 + vendor/rustix/src/backend/linux_raw/pid/mod.rs | 1 + .../rustix/src/backend/linux_raw/pid/syscalls.rs | 18 + vendor/rustix/src/backend/linux_raw/pipe/mod.rs | 2 + .../rustix/src/backend/linux_raw/pipe/syscalls.rs | 135 ++ vendor/rustix/src/backend/linux_raw/pipe/types.rs | 85 + vendor/rustix/src/backend/linux_raw/prctl/mod.rs | 1 + .../rustix/src/backend/linux_raw/prctl/syscalls.rs | 21 + vendor/rustix/src/backend/linux_raw/process/mod.rs | 3 + .../src/backend/linux_raw/process/syscalls.rs | 560 +++++++ .../rustix/src/backend/linux_raw/process/types.rs | 43 + .../rustix/src/backend/linux_raw/process/wait.rs | 123 ++ vendor/rustix/src/backend/linux_raw/pty/mod.rs | 1 + .../rustix/src/backend/linux_raw/pty/syscalls.rs | 43 + vendor/rustix/src/backend/linux_raw/rand/mod.rs | 2 + .../rustix/src/backend/linux_raw/rand/syscalls.rs | 15 + vendor/rustix/src/backend/linux_raw/rand/types.rs | 20 + vendor/rustix/src/backend/linux_raw/reg.rs | 259 +++ vendor/rustix/src/backend/linux_raw/runtime/mod.rs | 2 + .../src/backend/linux_raw/runtime/syscalls.rs | 346 ++++ vendor/rustix/src/backend/linux_raw/runtime/tls.rs | 7 + vendor/rustix/src/backend/linux_raw/shm/mod.rs | 2 + .../rustix/src/backend/linux_raw/shm/syscalls.rs | 46 + vendor/rustix/src/backend/linux_raw/shm/types.rs | 30 + vendor/rustix/src/backend/linux_raw/system/mod.rs | 2 + .../src/backend/linux_raw/system/syscalls.rs | 91 ++ .../rustix/src/backend/linux_raw/system/types.rs | 39 + vendor/rustix/src/backend/linux_raw/termios/mod.rs | 2 + .../src/backend/linux_raw/termios/syscalls.rs | 425 +++++ .../rustix/src/backend/linux_raw/termios/types.rs | 13 + .../rustix/src/backend/linux_raw/thread/cpu_set.rs | 51 + .../rustix/src/backend/linux_raw/thread/futex.rs | 89 + vendor/rustix/src/backend/linux_raw/thread/mod.rs | 4 + .../src/backend/linux_raw/thread/syscalls.rs | 549 +++++++ .../rustix/src/backend/linux_raw/thread/types.rs | 62 + vendor/rustix/src/backend/linux_raw/time/mod.rs | 2 + .../rustix/src/backend/linux_raw/time/syscalls.rs | 238 +++ vendor/rustix/src/backend/linux_raw/time/types.rs | 93 ++ vendor/rustix/src/backend/linux_raw/ugid/mod.rs | 1 + .../rustix/src/backend/linux_raw/ugid/syscalls.rs | 70 + vendor/rustix/src/backend/linux_raw/vdso.rs | 545 +++++++ .../rustix/src/backend/linux_raw/vdso_wrappers.rs | 623 +++++++ 94 files changed, 20954 insertions(+) create mode 100644 vendor/rustix/src/backend/linux_raw/arch/aarch64.rs create mode 100644 vendor/rustix/src/backend/linux_raw/arch/arm.rs create mode 100644 vendor/rustix/src/backend/linux_raw/arch/mips.rs create mode 100644 vendor/rustix/src/backend/linux_raw/arch/mips32r6.rs create mode 100644 vendor/rustix/src/backend/linux_raw/arch/mips64.rs create mode 100644 vendor/rustix/src/backend/linux_raw/arch/mips64r6.rs create mode 100644 vendor/rustix/src/backend/linux_raw/arch/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/arch/powerpc.rs create mode 100644 vendor/rustix/src/backend/linux_raw/arch/powerpc64.rs create mode 100644 vendor/rustix/src/backend/linux_raw/arch/riscv64.rs create mode 100644 vendor/rustix/src/backend/linux_raw/arch/s390x.rs create mode 100644 vendor/rustix/src/backend/linux_raw/arch/thumb.rs create mode 100644 vendor/rustix/src/backend/linux_raw/arch/x86.rs create mode 100644 vendor/rustix/src/backend/linux_raw/arch/x86_64.rs create mode 100644 vendor/rustix/src/backend/linux_raw/c.rs create mode 100644 vendor/rustix/src/backend/linux_raw/conv.rs create mode 100644 vendor/rustix/src/backend/linux_raw/event/epoll.rs create mode 100644 vendor/rustix/src/backend/linux_raw/event/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/event/poll_fd.rs create mode 100644 vendor/rustix/src/backend/linux_raw/event/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/event/types.rs create mode 100644 vendor/rustix/src/backend/linux_raw/fs/dir.rs create mode 100644 vendor/rustix/src/backend/linux_raw/fs/inotify.rs create mode 100644 vendor/rustix/src/backend/linux_raw/fs/makedev.rs create mode 100644 vendor/rustix/src/backend/linux_raw/fs/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/fs/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/fs/types.rs create mode 100644 vendor/rustix/src/backend/linux_raw/io/errno.rs create mode 100644 vendor/rustix/src/backend/linux_raw/io/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/io/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/io/types.rs create mode 100644 vendor/rustix/src/backend/linux_raw/io_uring/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/io_uring/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/mm/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/mm/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/mm/types.rs create mode 100644 vendor/rustix/src/backend/linux_raw/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/mount/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/mount/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/mount/types.rs create mode 100644 vendor/rustix/src/backend/linux_raw/net/addr.rs create mode 100644 vendor/rustix/src/backend/linux_raw/net/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/net/msghdr.rs create mode 100644 vendor/rustix/src/backend/linux_raw/net/netdevice.rs create mode 100644 vendor/rustix/src/backend/linux_raw/net/read_sockaddr.rs create mode 100644 vendor/rustix/src/backend/linux_raw/net/send_recv.rs create mode 100644 vendor/rustix/src/backend/linux_raw/net/sockopt.rs create mode 100644 vendor/rustix/src/backend/linux_raw/net/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/net/write_sockaddr.rs create mode 100644 vendor/rustix/src/backend/linux_raw/param/auxv.rs create mode 100644 vendor/rustix/src/backend/linux_raw/param/init.rs create mode 100644 vendor/rustix/src/backend/linux_raw/param/libc_auxv.rs create mode 100644 vendor/rustix/src/backend/linux_raw/param/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/pid/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/pid/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/pipe/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/pipe/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/pipe/types.rs create mode 100644 vendor/rustix/src/backend/linux_raw/prctl/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/prctl/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/process/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/process/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/process/types.rs create mode 100644 vendor/rustix/src/backend/linux_raw/process/wait.rs create mode 100644 vendor/rustix/src/backend/linux_raw/pty/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/pty/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/rand/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/rand/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/rand/types.rs create mode 100644 vendor/rustix/src/backend/linux_raw/reg.rs create mode 100644 vendor/rustix/src/backend/linux_raw/runtime/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/runtime/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/runtime/tls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/shm/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/shm/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/shm/types.rs create mode 100644 vendor/rustix/src/backend/linux_raw/system/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/system/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/system/types.rs create mode 100644 vendor/rustix/src/backend/linux_raw/termios/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/termios/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/termios/types.rs create mode 100644 vendor/rustix/src/backend/linux_raw/thread/cpu_set.rs create mode 100644 vendor/rustix/src/backend/linux_raw/thread/futex.rs create mode 100644 vendor/rustix/src/backend/linux_raw/thread/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/thread/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/thread/types.rs create mode 100644 vendor/rustix/src/backend/linux_raw/time/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/time/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/time/types.rs create mode 100644 vendor/rustix/src/backend/linux_raw/ugid/mod.rs create mode 100644 vendor/rustix/src/backend/linux_raw/ugid/syscalls.rs create mode 100644 vendor/rustix/src/backend/linux_raw/vdso.rs create mode 100644 vendor/rustix/src/backend/linux_raw/vdso_wrappers.rs (limited to 'vendor/rustix/src/backend/linux_raw') diff --git a/vendor/rustix/src/backend/linux_raw/arch/aarch64.rs b/vendor/rustix/src/backend/linux_raw/arch/aarch64.rs new file mode 100644 index 00000000..4f9e52af --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/arch/aarch64.rs @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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) +} diff --git a/vendor/rustix/src/backend/linux_raw/arch/arm.rs b/vendor/rustix/src/backend/linux_raw/arch/arm.rs new file mode 100644 index 00000000..4317154f --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/arch/arm.rs @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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) +} diff --git a/vendor/rustix/src/backend/linux_raw/arch/mips.rs b/vendor/rustix/src/backend/linux_raw/arch/mips.rs new file mode 100644 index 00000000..eb66e261 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/arch/mips.rs @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 + }) +} diff --git a/vendor/rustix/src/backend/linux_raw/arch/mips32r6.rs b/vendor/rustix/src/backend/linux_raw/arch/mips32r6.rs new file mode 100644 index 00000000..d273a968 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/arch/mips32r6.rs @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 + }) +} diff --git a/vendor/rustix/src/backend/linux_raw/arch/mips64.rs b/vendor/rustix/src/backend/linux_raw/arch/mips64.rs new file mode 100644 index 00000000..821812f2 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/arch/mips64.rs @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 + }) +} diff --git a/vendor/rustix/src/backend/linux_raw/arch/mips64r6.rs b/vendor/rustix/src/backend/linux_raw/arch/mips64r6.rs new file mode 100644 index 00000000..2a3cf564 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/arch/mips64r6.rs @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 + }) +} diff --git a/vendor/rustix/src/backend/linux_raw/arch/mod.rs b/vendor/rustix/src/backend/linux_raw/arch/mod.rs new file mode 100644 index 00000000..8d675adb --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/arch/mod.rs @@ -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(), + ) + }; +} diff --git a/vendor/rustix/src/backend/linux_raw/arch/powerpc.rs b/vendor/rustix/src/backend/linux_raw/arch/powerpc.rs new file mode 100644 index 00000000..481b49f4 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/arch/powerpc.rs @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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) +} diff --git a/vendor/rustix/src/backend/linux_raw/arch/powerpc64.rs b/vendor/rustix/src/backend/linux_raw/arch/powerpc64.rs new file mode 100644 index 00000000..f21ed6aa --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/arch/powerpc64.rs @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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) +} diff --git a/vendor/rustix/src/backend/linux_raw/arch/riscv64.rs b/vendor/rustix/src/backend/linux_raw/arch/riscv64.rs new file mode 100644 index 00000000..512657ac --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/arch/riscv64.rs @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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) +} diff --git a/vendor/rustix/src/backend/linux_raw/arch/s390x.rs b/vendor/rustix/src/backend/linux_raw/arch/s390x.rs new file mode 100644 index 00000000..5d6e14f1 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/arch/s390x.rs @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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) +} diff --git a/vendor/rustix/src/backend/linux_raw/arch/thumb.rs b/vendor/rustix/src/backend/linux_raw/arch/thumb.rs new file mode 100644 index 00000000..0989430d --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/arch/thumb.rs @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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) +} diff --git a/vendor/rustix/src/backend/linux_raw/arch/x86.rs b/vendor/rustix/src/backend/linux_raw/arch/x86.rs new file mode 100644 index 00000000..95376415 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/arch/x86.rs @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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) +} diff --git a/vendor/rustix/src/backend/linux_raw/arch/x86_64.rs b/vendor/rustix/src/backend/linux_raw/arch/x86_64.rs new file mode 100644 index 00000000..6d105c08 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/arch/x86_64.rs @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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) +} diff --git a/vendor/rustix/src/backend/linux_raw/c.rs b/vendor/rustix/src/backend/linux_raw/c.rs new file mode 100644 index 00000000..755dadcd --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/c.rs @@ -0,0 +1,385 @@ +//! 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::{FIONBIO, 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_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_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_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, + }, +}; + +// 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::*; diff --git a/vendor/rustix/src/backend/linux_raw/conv.rs b/vendor/rustix/src/backend/linux_raw/conv.rs new file mode 100644 index 00000000..901451ae --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/conv.rs @@ -0,0 +1,1021 @@ +//! Convert values to [`ArgReg`] and from [`RetReg`]. +//! +//! System call arguments and return values are all communicated with inline +//! asm and FFI as `*mut Opaque`. To protect these raw pointers from escaping +//! or being accidentally misused as they travel through the code, we wrap them +//! in [`ArgReg`] and [`RetReg`] structs. This file provides `From` +//! implementations and explicit conversion functions for converting values +//! into and out of these wrapper structs. +//! +//! # Safety +//! +//! Some of this code is `unsafe` in order to work with raw file descriptors, +//! and some is `unsafe` to interpret the values in a `RetReg`. +#![allow(unsafe_code)] + +use super::c; +use super::fd::{AsRawFd as _, BorrowedFd, FromRawFd as _, RawFd}; +#[cfg(any(feature = "event", feature = "runtime", feature = "system"))] +use super::io::errno::try_decode_error; +#[cfg(target_pointer_width = "64")] +use super::io::errno::try_decode_u64; +#[cfg(not(debug_assertions))] +use super::io::errno::{ + decode_c_int_infallible, decode_c_uint_infallible, decode_usize_infallible, +}; +use super::io::errno::{ + try_decode_c_int, try_decode_c_uint, try_decode_raw_fd, try_decode_usize, try_decode_void, + try_decode_void_star, +}; +use super::reg::{raw_arg, ArgNumber, ArgReg, RetReg, R0}; +#[cfg(feature = "time")] +use super::time::types::TimerfdClockId; +#[cfg(any(feature = "thread", feature = "time"))] +use crate::clockid::ClockId; +use crate::fd::OwnedFd; +use crate::ffi::CStr; +use crate::io; +#[cfg(any(feature = "process", feature = "runtime", feature = "termios"))] +use crate::pid::Pid; +#[cfg(feature = "process")] +use crate::process::Resource; +#[cfg(any(feature = "process", feature = "runtime"))] +use crate::signal::Signal; +use crate::utils::{as_mut_ptr, as_ptr}; +use core::mem::MaybeUninit; +use core::ptr::null_mut; +#[cfg(any(feature = "thread", feature = "time"))] +use linux_raw_sys::general::__kernel_clockid_t; +#[cfg(target_pointer_width = "64")] +use linux_raw_sys::general::__kernel_loff_t; +#[cfg(feature = "net")] +use linux_raw_sys::net::socklen_t; + +/// Convert `SYS_*` constants for socketcall. +#[cfg(target_arch = "x86")] +#[inline] +pub(super) fn x86_sys<'a, Num: ArgNumber>(sys: u32) -> ArgReg<'a, Num> { + pass_usize(sys as usize) +} + +/// Pass the "low" half of the endian-specific memory encoding of a `u64`, for +/// 32-bit architectures. +#[cfg(target_pointer_width = "32")] +#[inline] +pub(super) fn lo<'a, Num: ArgNumber>(x: u64) -> ArgReg<'a, Num> { + #[cfg(target_endian = "little")] + let x = x >> 32; + #[cfg(target_endian = "big")] + let x = x & 0xffff_ffff; + + pass_usize(x as usize) +} + +/// Pass the "high" half of the endian-specific memory encoding of a `u64`, for +/// 32-bit architectures. +#[cfg(target_pointer_width = "32")] +#[inline] +pub(super) fn hi<'a, Num: ArgNumber>(x: u64) -> ArgReg<'a, Num> { + #[cfg(target_endian = "little")] + let x = x & 0xffff_ffff; + #[cfg(target_endian = "big")] + let x = x >> 32; + + pass_usize(x as usize) +} + +/// Pass a zero, or null, argument. +#[inline] +pub(super) fn zero<'a, Num: ArgNumber>() -> ArgReg<'a, Num> { + raw_arg(null_mut()) +} + +/// Pass the `mem::size_of` of a type. +#[inline] +pub(super) fn size_of<'a, T: Sized, Num: ArgNumber>() -> ArgReg<'a, Num> { + pass_usize(core::mem::size_of::()) +} + +/// Pass an arbitrary `usize` value. +/// +/// For passing pointers, use `void_star` or other functions which take a raw +/// pointer instead of casting to `usize`, so that provenance is preserved. +#[inline] +pub(super) fn pass_usize<'a, Num: ArgNumber>(t: usize) -> ArgReg<'a, Num> { + raw_arg(t as *mut _) +} + +impl<'a, Num: ArgNumber, T> From<*mut T> for ArgReg<'a, Num> { + #[inline] + fn from(c: *mut T) -> Self { + raw_arg(c.cast()) + } +} + +impl<'a, Num: ArgNumber, T> From<*const T> for ArgReg<'a, Num> { + #[inline] + fn from(c: *const T) -> Self { + let mut_ptr = c as *mut T; + raw_arg(mut_ptr.cast()) + } +} + +impl<'a, Num: ArgNumber> From<&'a CStr> for ArgReg<'a, Num> { + #[inline] + fn from(c: &'a CStr) -> Self { + let mut_ptr = c.as_ptr() as *mut u8; + raw_arg(mut_ptr.cast()) + } +} + +impl<'a, Num: ArgNumber> From> for ArgReg<'a, Num> { + #[inline] + fn from(t: Option<&'a CStr>) -> Self { + raw_arg(match t { + Some(s) => { + let mut_ptr = s.as_ptr() as *mut u8; + mut_ptr.cast() + } + None => null_mut(), + }) + } +} + +/// Pass a borrowed file-descriptor argument. +impl<'a, Num: ArgNumber> From> for ArgReg<'a, Num> { + #[inline] + fn from(fd: BorrowedFd<'a>) -> Self { + // SAFETY: `BorrowedFd` ensures that the file descriptor is valid, and + // the lifetime parameter on the resulting `ArgReg` ensures that the + // result is bounded by the `BorrowedFd`'s lifetime. + unsafe { raw_fd(fd.as_raw_fd()) } + } +} + +/// Pass a raw file-descriptor argument. Most users should use [`ArgReg::from`] +/// instead, to preserve I/O safety as long as possible. +/// +/// # Safety +/// +/// `fd` must be a valid open file descriptor. +#[inline] +pub(super) unsafe fn raw_fd<'a, Num: ArgNumber>(fd: RawFd) -> ArgReg<'a, Num> { + // Use `no_fd` when passing `-1` is intended. + #[cfg(feature = "fs")] + debug_assert!(fd == crate::fs::CWD.as_raw_fd() || fd == crate::fs::ABS.as_raw_fd() || fd >= 0); + + // Don't pass the `IORING_REGISTER_FILES_SKIP` sentry value this way. + #[cfg(feature = "io_uring")] + debug_assert_ne!(fd, crate::io_uring::IORING_REGISTER_FILES_SKIP.as_raw_fd()); + + // Linux doesn't look at the high bits beyond the `c_int`, so use + // zero-extension rather than sign-extension because it's a smaller + // instruction. + let fd: c::c_int = fd; + pass_usize(fd as c::c_uint as usize) +} + +/// Deliberately pass `-1` to a file-descriptor argument, for system calls +/// like `mmap` where this indicates the argument is omitted. +#[inline] +pub(super) fn no_fd<'a, Num: ArgNumber>() -> ArgReg<'a, Num> { + pass_usize(!0_usize) +} + +#[inline] +pub(super) fn slice_just_addr(v: &[T]) -> ArgReg<'_, Num> { + let mut_ptr = v.as_ptr() as *mut T; + raw_arg(mut_ptr.cast()) +} + +#[inline] +pub(super) fn slice_just_addr_mut(v: &mut [T]) -> ArgReg<'_, Num> { + raw_arg(v.as_mut_ptr().cast()) +} + +#[inline] +pub(super) fn slice( + v: &[T], +) -> (ArgReg<'_, Num0>, ArgReg<'_, Num1>) { + (slice_just_addr(v), pass_usize(v.len())) +} + +#[inline] +pub(super) fn slice_mut( + v: &mut [T], +) -> (ArgReg<'_, Num0>, ArgReg<'_, Num1>) { + (raw_arg(v.as_mut_ptr().cast()), pass_usize(v.len())) +} + +#[inline] +pub(super) fn by_ref(t: &T) -> ArgReg<'_, Num> { + let mut_ptr = as_ptr(t) as *mut T; + raw_arg(mut_ptr.cast()) +} + +#[inline] +pub(super) fn by_mut(t: &mut T) -> ArgReg<'_, Num> { + raw_arg(as_mut_ptr(t).cast()) +} + +/// Convert an optional mutable reference into a `usize` for passing to a +/// syscall. +#[inline] +pub(super) fn opt_mut(t: Option<&mut T>) -> ArgReg<'_, Num> { + // This optimizes into the equivalent of `transmute(t)`, and has the + // advantage of not requiring `unsafe`. + match t { + Some(t) => by_mut(t), + None => raw_arg(null_mut()), + } +} + +/// Convert an optional immutable reference into a `usize` for passing to a +/// syscall. +#[inline] +pub(super) fn opt_ref(t: Option<&T>) -> ArgReg<'_, Num> { + // This optimizes into the equivalent of `transmute(t)`, and has the + // advantage of not requiring `unsafe`. + match t { + Some(t) => by_ref(t), + None => raw_arg(null_mut()), + } +} + +/// Convert a `c_int` into an `ArgReg`. +/// +/// Be sure to use `raw_fd` to pass `RawFd` values. +#[inline] +pub(super) fn c_int<'a, Num: ArgNumber>(i: c::c_int) -> ArgReg<'a, Num> { + pass_usize(i as usize) +} + +/// Convert a `c_uint` into an `ArgReg`. +#[inline] +pub(super) fn c_uint<'a, Num: ArgNumber>(i: c::c_uint) -> ArgReg<'a, Num> { + pass_usize(i as usize) +} + +#[cfg(target_pointer_width = "64")] +#[inline] +pub(super) fn loff_t<'a, Num: ArgNumber>(i: __kernel_loff_t) -> ArgReg<'a, Num> { + pass_usize(i as usize) +} + +#[cfg(target_pointer_width = "64")] +#[inline] +pub(super) fn loff_t_from_u64<'a, Num: ArgNumber>(i: u64) -> ArgReg<'a, Num> { + // `loff_t` is signed, but syscalls which expect `loff_t` return `EINVAL` + // if it's outside the signed `i64` range, so we can silently cast. + pass_usize(i as usize) +} + +#[cfg(any(feature = "thread", feature = "time"))] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(i: ClockId) -> Self { + pass_usize(i as __kernel_clockid_t as usize) + } +} + +#[cfg(feature = "time")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(i: TimerfdClockId) -> Self { + pass_usize(i as __kernel_clockid_t as usize) + } +} + +#[cfg(feature = "net")] +#[inline] +pub(super) fn socklen_t<'a, Num: ArgNumber>(i: socklen_t) -> ArgReg<'a, Num> { + pass_usize(i as usize) +} + +#[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) mod fs { + use super::*; + use crate::fs::{FileType, Mode, OFlags}; + #[cfg(target_pointer_width = "32")] + use linux_raw_sys::general::O_LARGEFILE; + + impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(mode: Mode) -> Self { + pass_usize(mode.bits() as usize) + } + } + + impl<'a, Num: ArgNumber> From<(Mode, FileType)> for ArgReg<'a, Num> { + #[inline] + fn from(pair: (Mode, FileType)) -> Self { + pass_usize(pair.0.as_raw_mode() as usize | pair.1.as_raw_mode() as usize) + } + } + + impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::fs::AtFlags) -> Self { + c_uint(flags.bits()) + } + } + + impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::fs::XattrFlags) -> Self { + c_uint(flags.bits()) + } + } + + impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::fs::inotify::CreateFlags) -> Self { + c_uint(flags.bits()) + } + } + + impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::fs::inotify::WatchFlags) -> Self { + c_uint(flags.bits()) + } + } + + impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::fs::MemfdFlags) -> Self { + c_uint(flags.bits()) + } + } + + impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::fs::RenameFlags) -> Self { + c_uint(flags.bits()) + } + } + + impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::fs::StatxFlags) -> Self { + c_uint(flags.bits()) + } + } + + #[cfg(target_pointer_width = "32")] + #[inline] + fn oflags_bits(oflags: OFlags) -> c::c_uint { + let mut bits = oflags.bits(); + // Add `O_LARGEFILE`, unless `O_PATH` is set, as Linux returns `EINVAL` + // when both are set. + if !oflags.contains(OFlags::PATH) { + bits |= O_LARGEFILE; + } + bits + } + + #[cfg(target_pointer_width = "64")] + #[inline] + const fn oflags_bits(oflags: OFlags) -> c::c_uint { + oflags.bits() + } + + impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(oflags: OFlags) -> Self { + pass_usize(oflags_bits(oflags) as usize) + } + } + + /// Convert an `OFlags` into a `u64` for use in the `open_how` struct. + #[inline] + pub(crate) fn oflags_for_open_how(oflags: OFlags) -> u64 { + u64::from(oflags_bits(oflags)) + } + + impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::fs::FallocateFlags) -> Self { + c_uint(flags.bits()) + } + } + + impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(advice: crate::fs::Advice) -> Self { + c_uint(advice as c::c_uint) + } + } + + impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::fs::SealFlags) -> Self { + c_uint(flags.bits()) + } + } + + impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(access: crate::fs::Access) -> Self { + c_uint(access.bits()) + } + } +} + +#[cfg(feature = "mount")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::backend::mount::types::MountFlagsArg) -> Self { + c_uint(flags.0) + } +} + +#[cfg(feature = "mount")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::backend::mount::types::UnmountFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "mount")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(cmd: crate::mount::FsConfigCmd) -> Self { + c_uint(cmd as c::c_uint) + } +} + +#[cfg(feature = "mount")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::backend::mount::types::FsOpenFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "mount")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::backend::mount::types::FsMountFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "mount")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::backend::mount::types::MountAttrFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "mount")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::backend::mount::types::OpenTreeFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "mount")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::backend::mount::types::FsPickFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "mount")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::backend::mount::types::MoveMountFlags) -> Self { + c_uint(flags.bits()) + } +} + +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::io::FdFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "pipe")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::pipe::PipeFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "pipe")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::pipe::SpliceFlags) -> Self { + c_uint(flags.bits()) + } +} + +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::io::DupFlags) -> Self { + c_uint(flags.bits()) + } +} + +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::io::ReadWriteFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "process")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::process::PidfdFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "pty")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::pty::OpenptFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "thread")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::thread::UnshareFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "event")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::event::EventfdFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "event")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::event::epoll::CreateFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "mm")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::backend::mm::types::ProtFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "mm")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::backend::mm::types::MsyncFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "mm")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::backend::mm::types::MremapFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "mm")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::backend::mm::types::MlockFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "mm")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::backend::mm::types::MlockAllFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "mm")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::backend::mm::types::MapFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "mm")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::backend::mm::types::MprotectFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "mm")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::backend::mm::types::UserfaultfdFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "thread")] +impl<'a, Num: ArgNumber> From + for ArgReg<'a, Num> +{ + #[inline] + fn from(cmd: crate::backend::thread::types::MembarrierCommand) -> Self { + c_uint(cmd as u32) + } +} + +#[cfg(feature = "thread")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(cpuid: crate::thread::Cpuid) -> Self { + c_uint(cpuid.as_raw()) + } +} + +#[cfg(target_pointer_width = "64")] +#[inline] +pub(super) fn dev_t<'a, Num: ArgNumber>(dev: u64) -> ArgReg<'a, Num> { + pass_usize(dev as usize) +} + +#[cfg(target_pointer_width = "32")] +#[inline] +pub(super) fn dev_t<'a, Num: ArgNumber>(dev: u64) -> io::Result> { + Ok(pass_usize(dev.try_into().map_err(|_err| io::Errno::INVAL)?)) +} + +/// Convert a `Resource` into a syscall argument. +#[cfg(feature = "process")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(resource: Resource) -> Self { + c_uint(resource as c::c_uint) + } +} + +#[cfg(any(feature = "process", feature = "runtime", feature = "termios"))] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(pid: Pid) -> Self { + pass_usize(pid.as_raw_nonzero().get() as usize) + } +} + +#[cfg(feature = "process")] +#[inline] +pub(super) fn negative_pid<'a, Num: ArgNumber>(pid: Pid) -> ArgReg<'a, Num> { + pass_usize(pid.as_raw_nonzero().get().wrapping_neg() as usize) +} + +#[cfg(any(feature = "process", feature = "runtime"))] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(sig: Signal) -> Self { + pass_usize(sig.as_raw() as usize) + } +} + +#[cfg(feature = "io_uring")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::io_uring::IoringEnterFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "time")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::time::TimerfdFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "time")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::time::TimerfdTimerFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "rand")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::rand::GetRandomFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "net")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::net::RecvFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "net")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::net::SendFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "net")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::net::SocketFlags) -> Self { + c_uint(flags.bits()) + } +} + +#[cfg(feature = "net")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(family: crate::net::AddressFamily) -> Self { + c_uint(family.0.into()) + } +} + +#[cfg(feature = "net")] +impl<'a, Num: ArgNumber> From<(crate::net::SocketType, crate::net::SocketFlags)> + for ArgReg<'a, Num> +{ + #[inline] + fn from(pair: (crate::net::SocketType, crate::net::SocketFlags)) -> Self { + c_uint(pair.0 .0 | pair.1.bits()) + } +} + +#[cfg(feature = "thread")] +impl<'a, Num: ArgNumber> + From<( + crate::backend::thread::futex::Operation, + crate::thread::futex::Flags, + )> for ArgReg<'a, Num> +{ + #[inline] + fn from( + pair: ( + crate::backend::thread::futex::Operation, + crate::thread::futex::Flags, + ), + ) -> Self { + c_uint(pair.0 as u32 | pair.1.bits()) + } +} + +#[cfg(feature = "net")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(type_: crate::net::SocketType) -> Self { + c_uint(type_.0) + } +} + +#[cfg(feature = "net")] +impl<'a, Num: ArgNumber> From> for ArgReg<'a, Num> { + #[inline] + fn from(protocol: Option) -> Self { + c_uint(match protocol { + Some(p) => p.0.get(), + None => 0, + }) + } +} + +impl<'a, Num: ArgNumber, T> From<&'a mut MaybeUninit> for ArgReg<'a, Num> { + #[inline] + fn from(t: &'a mut MaybeUninit) -> Self { + raw_arg(t.as_mut_ptr().cast()) + } +} + +impl<'a, Num: ArgNumber, T> From<&'a mut [MaybeUninit]> for ArgReg<'a, Num> { + #[inline] + fn from(t: &'a mut [MaybeUninit]) -> Self { + raw_arg(t.as_mut_ptr().cast()) + } +} + +#[cfg(any(feature = "process", feature = "thread"))] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(t: crate::ugid::Uid) -> Self { + c_uint(t.as_raw()) + } +} + +#[cfg(any(feature = "process", feature = "thread"))] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(t: crate::ugid::Gid) -> Self { + c_uint(t.as_raw()) + } +} + +#[cfg(feature = "runtime")] +impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { + #[inline] + fn from(flags: crate::runtime::How) -> Self { + c_uint(flags as u32) + } +} + +/// Convert a `usize` returned from a syscall that effectively returns `()` on +/// success. +/// +/// # Safety +/// +/// The caller must ensure that this is the return value of a syscall which +/// just returns 0 on success. +#[inline] +pub(super) unsafe fn ret(raw: RetReg) -> io::Result<()> { + try_decode_void(raw) +} + +/// Convert a `usize` returned from a syscall that doesn't return on success. +/// +/// # Safety +/// +/// The caller must ensure that this is the return value of a syscall which +/// doesn't return on success. +#[cfg(any(feature = "event", feature = "runtime", feature = "system"))] +#[inline] +pub(super) unsafe fn ret_error(raw: RetReg) -> io::Errno { + try_decode_error(raw) +} + +/// Convert a `usize` returned from a syscall that effectively always returns +/// `()`. +/// +/// # Safety +/// +/// The caller must ensure that this is the return value of a syscall which +/// always returns `()`. +#[inline] +pub(super) unsafe fn ret_infallible(raw: RetReg) { + #[cfg(debug_assertions)] + { + try_decode_void(raw).unwrap() + } + #[cfg(not(debug_assertions))] + drop(raw); +} + +/// Convert a `usize` returned from a syscall that effectively returns a +/// `c_int` on success. +#[inline] +pub(super) fn ret_c_int(raw: RetReg) -> io::Result { + try_decode_c_int(raw) +} + +/// Convert a `usize` returned from a syscall that effectively returns a +/// `c_uint` on success. +#[inline] +pub(super) fn ret_c_uint(raw: RetReg) -> io::Result { + try_decode_c_uint(raw) +} + +/// Convert a `usize` returned from a syscall that effectively returns a `u64` +/// on success. +#[cfg(target_pointer_width = "64")] +#[inline] +pub(super) fn ret_u64(raw: RetReg) -> io::Result { + try_decode_u64(raw) +} + +/// Convert a `usize` returned from a syscall that effectively returns a +/// `usize` on success. +#[inline] +pub(super) fn ret_usize(raw: RetReg) -> io::Result { + try_decode_usize(raw) +} + +/// Convert a `usize` returned from a syscall that effectively always +/// returns a `usize`. +/// +/// # Safety +/// +/// This function must only be used with return values from infallible +/// syscalls. +#[inline] +pub(super) unsafe fn ret_usize_infallible(raw: RetReg) -> usize { + #[cfg(debug_assertions)] + { + try_decode_usize(raw).unwrap() + } + #[cfg(not(debug_assertions))] + { + decode_usize_infallible(raw) + } +} + +/// Convert a `c_int` returned from a syscall that effectively always +/// returns a `c_int`. +/// +/// # Safety +/// +/// This function must only be used with return values from infallible +/// syscalls. +#[inline] +pub(super) unsafe fn ret_c_int_infallible(raw: RetReg) -> c::c_int { + #[cfg(debug_assertions)] + { + try_decode_c_int(raw).unwrap() + } + #[cfg(not(debug_assertions))] + { + decode_c_int_infallible(raw) + } +} + +/// Convert a `c_uint` returned from a syscall that effectively always +/// returns a `c_uint`. +/// +/// # Safety +/// +/// This function must only be used with return values from infallible +/// syscalls. +#[inline] +pub(super) unsafe fn ret_c_uint_infallible(raw: RetReg) -> c::c_uint { + #[cfg(debug_assertions)] + { + try_decode_c_uint(raw).unwrap() + } + #[cfg(not(debug_assertions))] + { + decode_c_uint_infallible(raw) + } +} + +/// Convert a `usize` returned from a syscall that effectively returns an +/// `OwnedFd` on success. +/// +/// # Safety +/// +/// The caller must ensure that this is the return value of a syscall which +/// returns an owned file descriptor. +#[inline] +pub(super) unsafe fn ret_owned_fd(raw: RetReg) -> io::Result { + let raw_fd = try_decode_raw_fd(raw)?; + Ok(crate::backend::fd::OwnedFd::from_raw_fd(raw_fd)) +} + +/// Convert the return value of `dup2` and `dup3`. +/// +/// When these functions succeed, they return the same value as their second +/// argument, so we don't construct a new `OwnedFd`. +/// +/// # Safety +/// +/// The caller must ensure that this is the return value of a syscall which +/// returns a file descriptor. +#[inline] +pub(super) unsafe fn ret_discarded_fd(raw: RetReg) -> io::Result<()> { + let _raw_fd = try_decode_raw_fd(raw)?; + Ok(()) +} + +/// Convert a `usize` returned from a syscall that effectively returns a +/// `*mut c_void` on success. +#[inline] +pub(super) fn ret_void_star(raw: RetReg) -> io::Result<*mut c::c_void> { + try_decode_void_star(raw) +} diff --git a/vendor/rustix/src/backend/linux_raw/event/epoll.rs b/vendor/rustix/src/backend/linux_raw/event/epoll.rs new file mode 100644 index 00000000..093129db --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/event/epoll.rs @@ -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; + + /// + 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; + + /// + const _ = !0; + } +} diff --git a/vendor/rustix/src/backend/linux_raw/event/mod.rs b/vendor/rustix/src/backend/linux_raw/event/mod.rs new file mode 100644 index 00000000..605de253 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/event/mod.rs @@ -0,0 +1,4 @@ +pub mod epoll; +pub(crate) mod poll_fd; +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/linux_raw/event/poll_fd.rs b/vendor/rustix/src/backend/linux_raw/event/poll_fd.rs new file mode 100644 index 00000000..9de43f26 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/event/poll_fd.rs @@ -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; + + /// + 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: &'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(&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() + } +} diff --git a/vendor/rustix/src/backend/linux_raw/event/syscalls.rs b/vendor/rustix/src/backend/linux_raw/event/syscalls.rs new file mode 100644 index 00000000..8a4e24bd --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/event/syscalls.rs @@ -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 { + 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::() + )); + } + } + + // 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::() + )) + } + + #[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::() + )) + } +} + +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 { + 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 { + // 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 { + // 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 { + 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); + } +} diff --git a/vendor/rustix/src/backend/linux_raw/event/types.rs b/vendor/rustix/src/backend/linux_raw/event/types.rs new file mode 100644 index 00000000..9d320dfe --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/event/types.rs @@ -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; + + /// + const _ = !0; + } +} diff --git a/vendor/rustix/src/backend/linux_raw/fs/dir.rs b/vendor/rustix/src/backend/linux_raw/fs/dir.rs new file mode 100644 index 00000000..31ebc543 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/fs/dir.rs @@ -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, + + /// 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: Fd) -> io::Result { + Self::_new(fd.into()) + } + + #[inline] + fn _new(fd: OwnedFd) -> io::Result { + 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: Fd) -> io::Result { + Self::_read_from(fd.as_fd()) + } + + #[inline] + fn _read_from(fd: BorrowedFd<'_>) -> io::Result { + 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> { + // 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::() { + 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> { + // 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::() { + self.buf.reserve(32 * size_of::()); + } + 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 { + fstat(&self.fd) + } + + /// `fstatfs(self)` + #[inline] + pub fn statfs(&self) -> io::Result { + fstatfs(&self.fd) + } + + /// `fstatvfs(self)` + #[inline] + pub fn statvfs(&self) -> io::Result { + 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; + + #[inline] + fn next(&mut self) -> Option { + 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()); + } +} diff --git a/vendor/rustix/src/backend/linux_raw/fs/inotify.rs b/vendor/rustix/src/backend/linux_raw/fs/inotify.rs new file mode 100644 index 00000000..eb13d910 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/fs/inotify.rs @@ -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; + + /// + 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; + + /// + 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; + + /// + const _ = !0; + } +} diff --git a/vendor/rustix/src/backend/linux_raw/fs/makedev.rs b/vendor/rustix/src/backend/linux_raw/fs/makedev.rs new file mode 100644 index 00000000..284ba2f1 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/fs/makedev.rs @@ -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 +} diff --git a/vendor/rustix/src/backend/linux_raw/fs/mod.rs b/vendor/rustix/src/backend/linux_raw/fs/mod.rs new file mode 100644 index 00000000..9f53c5db --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/fs/mod.rs @@ -0,0 +1,13 @@ +#[cfg(feature = "alloc")] +pub(crate) mod dir; +pub mod inotify; +pub(crate) mod makedev; +pub(crate) mod syscalls; +pub(crate) mod types; + +// TODO: Fix linux-raw-sys to define ioctl codes for sparc. +#[cfg(any(target_arch = "sparc", target_arch = "sparc64"))] +pub(crate) const EXT4_IOC_RESIZE_FS: u32 = 0x8008_6610; + +#[cfg(not(any(target_arch = "sparc", target_arch = "sparc64")))] +pub(crate) use linux_raw_sys::ioctl::EXT4_IOC_RESIZE_FS; diff --git a/vendor/rustix/src/backend/linux_raw/fs/syscalls.rs b/vendor/rustix/src/backend/linux_raw/fs/syscalls.rs new file mode 100644 index 00000000..872dd8e3 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/fs/syscalls.rs @@ -0,0 +1,1722 @@ +//! linux_raw syscalls supporting `rustix::fs`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code)] +#![allow(clippy::undocumented_unsafe_blocks)] + +use crate::backend::c; +use crate::backend::conv::fs::oflags_for_open_how; +#[cfg(any( + not(feature = "linux_4_11"), + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "mips", + target_arch = "mips32r6", +))] +use crate::backend::conv::zero; +use crate::backend::conv::{ + by_ref, c_int, c_uint, dev_t, opt_mut, pass_usize, raw_fd, ret, ret_c_int, ret_c_uint, + ret_infallible, ret_owned_fd, ret_usize, size_of, slice, slice_mut, +}; +#[cfg(target_pointer_width = "64")] +use crate::backend::conv::{loff_t, loff_t_from_u64, ret_u64}; +use crate::fd::{BorrowedFd, OwnedFd}; +use crate::ffi::CStr; +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] +use crate::fs::CWD; +use crate::fs::{ + inotify, Access, Advice, AtFlags, FallocateFlags, FileType, FlockOperation, Fsid, Gid, + MemfdFlags, Mode, OFlags, RenameFlags, ResolveFlags, SealFlags, SeekFrom, Stat, StatFs, + StatVfs, StatVfsMountFlags, Statx, StatxFlags, Timestamps, Uid, XattrFlags, +}; +use crate::io; +use core::mem::MaybeUninit; +use core::num::NonZeroU64; +#[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] +use linux_raw_sys::general::stat as linux_stat64; +use linux_raw_sys::general::{ + open_how, AT_EACCESS, AT_FDCWD, AT_REMOVEDIR, AT_SYMLINK_NOFOLLOW, F_ADD_SEALS, F_GETFL, + F_GET_SEALS, F_SETFL, SEEK_CUR, SEEK_DATA, SEEK_END, SEEK_HOLE, SEEK_SET, STATX__RESERVED, +}; +#[cfg(target_pointer_width = "32")] +use { + crate::backend::conv::{hi, lo, slice_just_addr}, + linux_raw_sys::general::stat64 as linux_stat64, + linux_raw_sys::general::timespec as __kernel_old_timespec, +}; + +#[inline] +pub(crate) fn open(path: &CStr, flags: OFlags, mode: Mode) -> io::Result { + // Always enable support for large files. + let flags = flags | OFlags::LARGEFILE; + + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + { + openat(CWD, path, flags, mode) + } + #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] + unsafe { + ret_owned_fd(syscall_readonly!(__NR_open, path, flags, mode)) + } +} + +#[inline] +pub(crate) fn openat( + dirfd: BorrowedFd<'_>, + path: &CStr, + flags: OFlags, + mode: Mode, +) -> io::Result { + // Always enable support for large files. + let flags = flags | OFlags::LARGEFILE; + + unsafe { ret_owned_fd(syscall_readonly!(__NR_openat, dirfd, path, flags, mode)) } +} + +#[inline] +pub(crate) fn openat2( + dirfd: BorrowedFd<'_>, + path: &CStr, + mut flags: OFlags, + mode: Mode, + resolve: ResolveFlags, +) -> io::Result { + // Enable support for large files, but not with `O_PATH` because + // `openat2` doesn't like those flags together. + if !flags.contains(OFlags::PATH) { + flags |= OFlags::from_bits_retain(c::O_LARGEFILE); + } + + unsafe { + ret_owned_fd(syscall_readonly!( + __NR_openat2, + dirfd, + path, + by_ref(&open_how { + flags: oflags_for_open_how(flags), + mode: u64::from(mode.bits()), + resolve: resolve.bits(), + }), + size_of::() + )) + } +} + +#[inline] +pub(crate) fn chmod(path: &CStr, mode: Mode) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_fchmodat, + raw_fd(AT_FDCWD), + path, + mode + )) + } +} + +#[inline] +pub(crate) fn chmodat( + dirfd: BorrowedFd<'_>, + path: &CStr, + mode: Mode, + flags: AtFlags, +) -> io::Result<()> { + if flags == AtFlags::SYMLINK_NOFOLLOW { + return Err(io::Errno::OPNOTSUPP); + } + if !flags.is_empty() { + return Err(io::Errno::INVAL); + } + unsafe { ret(syscall_readonly!(__NR_fchmodat, dirfd, path, mode)) } +} + +#[inline] +pub(crate) fn fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_fchmod, fd, mode)) } +} + +#[inline] +pub(crate) fn chownat( + dirfd: BorrowedFd<'_>, + path: &CStr, + owner: Option, + group: Option, + flags: AtFlags, +) -> io::Result<()> { + unsafe { + let (ow, gr) = crate::ugid::translate_fchown_args(owner, group); + ret(syscall_readonly!( + __NR_fchownat, + dirfd, + path, + c_uint(ow), + c_uint(gr), + flags + )) + } +} + +#[inline] +pub(crate) fn chown(path: &CStr, owner: Option, group: Option) -> io::Result<()> { + // Most architectures have a `chown` syscall. + #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] + unsafe { + let (ow, gr) = crate::ugid::translate_fchown_args(owner, group); + ret(syscall_readonly!(__NR_chown, path, c_uint(ow), c_uint(gr))) + } + + // Aarch64 and RISC-V don't, so use `fchownat`. + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + unsafe { + let (ow, gr) = crate::ugid::translate_fchown_args(owner, group); + ret(syscall_readonly!( + __NR_fchownat, + raw_fd(AT_FDCWD), + path, + c_uint(ow), + c_uint(gr), + zero() + )) + } +} + +#[inline] +pub(crate) fn fchown(fd: BorrowedFd<'_>, owner: Option, group: Option) -> io::Result<()> { + unsafe { + let (ow, gr) = crate::ugid::translate_fchown_args(owner, group); + ret(syscall_readonly!(__NR_fchown, fd, c_uint(ow), c_uint(gr))) + } +} + +#[inline] +pub(crate) fn mknodat( + dirfd: BorrowedFd<'_>, + path: &CStr, + file_type: FileType, + mode: Mode, + dev: u64, +) -> io::Result<()> { + #[cfg(target_pointer_width = "32")] + unsafe { + ret(syscall_readonly!( + __NR_mknodat, + dirfd, + path, + (mode, file_type), + dev_t(dev)? + )) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret(syscall_readonly!( + __NR_mknodat, + dirfd, + path, + (mode, file_type), + dev_t(dev) + )) + } +} + +#[inline] +pub(crate) fn seek(fd: BorrowedFd<'_>, pos: SeekFrom) -> io::Result { + let (whence, offset) = match pos { + SeekFrom::Start(pos) => { + let pos: u64 = pos; + // Silently cast; we'll get `EINVAL` if the value is negative. + (SEEK_SET, pos as i64) + } + SeekFrom::End(offset) => (SEEK_END, offset), + SeekFrom::Current(offset) => (SEEK_CUR, offset), + SeekFrom::Data(pos) => { + let pos: u64 = pos; + // Silently cast; we'll get `EINVAL` if the value is negative. + (SEEK_DATA, pos as i64) + } + SeekFrom::Hole(pos) => { + let pos: u64 = pos; + // Silently cast; we'll get `EINVAL` if the value is negative. + (SEEK_HOLE, pos as i64) + } + }; + _seek(fd, offset, whence) +} + +#[inline] +pub(crate) fn _seek(fd: BorrowedFd<'_>, offset: i64, whence: c::c_uint) -> io::Result { + #[cfg(target_pointer_width = "32")] + unsafe { + let mut result = MaybeUninit::::uninit(); + ret(syscall!( + __NR__llseek, + fd, + // Don't use the hi/lo functions here because Linux's llseek + // takes its 64-bit argument differently from everything else. + pass_usize((offset >> 32) as usize), + pass_usize(offset as usize), + &mut result, + c_uint(whence) + ))?; + Ok(result.assume_init()) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret_u64(syscall_readonly!( + __NR_lseek, + fd, + loff_t(offset), + c_uint(whence) + )) + } +} + +#[inline] +pub(crate) fn tell(fd: BorrowedFd<'_>) -> io::Result { + _seek(fd, 0, SEEK_CUR).map(|x| x as u64) +} + +#[inline] +pub(crate) fn ftruncate(fd: BorrowedFd<'_>, length: u64) -> io::Result<()> { + // + #[cfg(all( + target_pointer_width = "32", + any( + target_arch = "arm", + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "powerpc" + ), + ))] + unsafe { + ret(syscall_readonly!( + __NR_ftruncate64, + fd, + zero(), + hi(length), + lo(length) + )) + } + #[cfg(all( + target_pointer_width = "32", + not(any( + target_arch = "arm", + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "powerpc" + )), + ))] + unsafe { + ret(syscall_readonly!( + __NR_ftruncate64, + fd, + hi(length), + lo(length) + )) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret(syscall_readonly!( + __NR_ftruncate, + fd, + loff_t_from_u64(length) + )) + } +} + +#[inline] +pub(crate) fn fallocate( + fd: BorrowedFd<'_>, + mode: FallocateFlags, + offset: u64, + len: u64, +) -> io::Result<()> { + #[cfg(target_pointer_width = "32")] + unsafe { + ret(syscall_readonly!( + __NR_fallocate, + fd, + mode, + hi(offset), + lo(offset), + hi(len), + lo(len) + )) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret(syscall_readonly!( + __NR_fallocate, + fd, + mode, + loff_t_from_u64(offset), + loff_t_from_u64(len) + )) + } +} + +#[inline] +pub(crate) fn fadvise( + fd: BorrowedFd<'_>, + pos: u64, + len: Option, + advice: Advice, +) -> io::Result<()> { + let len = match len { + None => 0, + Some(len) => len.get(), + }; + + // On ARM, the arguments are reordered so that the `len` and `pos` argument + // pairs are aligned. And ARM has a custom syscall code for this. + #[cfg(target_arch = "arm")] + unsafe { + ret(syscall_readonly!( + __NR_arm_fadvise64_64, + fd, + advice, + hi(pos), + lo(pos), + hi(len), + lo(len) + )) + } + + // On powerpc, the arguments are reordered as on ARM. + #[cfg(target_arch = "powerpc")] + unsafe { + ret(syscall_readonly!( + __NR_fadvise64_64, + fd, + advice, + hi(pos), + lo(pos), + hi(len), + lo(len) + )) + } + + // On mips, the arguments are not reordered, and padding is inserted + // instead to ensure alignment. + #[cfg(any(target_arch = "mips", target_arch = "mips32r6"))] + unsafe { + ret(syscall_readonly!( + __NR_fadvise64, + fd, + zero(), + hi(pos), + lo(pos), + hi(len), + lo(len), + advice + )) + } + + // For all other 32-bit architectures, use `fadvise64_64` so that we get a + // 64-bit length. + #[cfg(all( + target_pointer_width = "32", + not(any( + target_arch = "arm", + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "powerpc" + )), + ))] + unsafe { + ret(syscall_readonly!( + __NR_fadvise64_64, + fd, + hi(pos), + lo(pos), + hi(len), + lo(len), + advice + )) + } + + // On 64-bit architectures, use `fadvise64` which is sufficient. + #[cfg(target_pointer_width = "64")] + unsafe { + ret(syscall_readonly!( + __NR_fadvise64, + fd, + loff_t_from_u64(pos), + loff_t_from_u64(len), + advice + )) + } +} + +#[inline] +pub(crate) fn fsync(fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_fsync, fd)) } +} + +#[inline] +pub(crate) fn fdatasync(fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_fdatasync, fd)) } +} + +#[inline] +pub(crate) fn flock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_flock, + fd, + c_uint(operation as c::c_uint) + )) + } +} + +#[inline] +pub(crate) fn syncfs(fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_syncfs, fd)) } +} + +#[inline] +pub(crate) fn sync() { + unsafe { ret_infallible(syscall_readonly!(__NR_sync)) } +} + +#[inline] +pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result { + // 32-bit and mips64 Linux: `struct stat64` is not y2038 compatible; use + // `statx`. + // + // And, some old platforms don't support `statx`, and some fail with a + // confusing error code, so we call `crate::fs::statx` to handle that. If + // `statx` isn't available, fall back to the buggy system call. + #[cfg(any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" + ))] + { + match crate::fs::statx(fd, cstr!(""), AtFlags::EMPTY_PATH, StatxFlags::BASIC_STATS) { + Ok(x) => statx_to_stat(x), + Err(io::Errno::NOSYS) => fstat_old(fd), + Err(err) => Err(err), + } + } + + #[cfg(all( + target_pointer_width = "64", + not(target_arch = "mips64"), + not(target_arch = "mips64r6") + ))] + unsafe { + let mut result = MaybeUninit::::uninit(); + ret(syscall!(__NR_fstat, fd, &mut result))?; + Ok(result.assume_init()) + } +} + +#[cfg(any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6", +))] +fn fstat_old(fd: BorrowedFd<'_>) -> io::Result { + let mut result = MaybeUninit::::uninit(); + + #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] + unsafe { + ret(syscall!(__NR_fstat, fd, &mut result))?; + stat_to_stat(result.assume_init()) + } + + #[cfg(target_pointer_width = "32")] + unsafe { + ret(syscall!(__NR_fstat64, fd, &mut result))?; + stat_to_stat(result.assume_init()) + } +} + +#[inline] +pub(crate) fn stat(path: &CStr) -> io::Result { + // See the comments in `fstat` about using `crate::fs::statx` here. + #[cfg(any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" + ))] + { + match crate::fs::statx( + crate::fs::CWD, + path, + AtFlags::empty(), + StatxFlags::BASIC_STATS, + ) { + Ok(x) => statx_to_stat(x), + Err(io::Errno::NOSYS) => stat_old(path), + Err(err) => Err(err), + } + } + + #[cfg(all( + target_pointer_width = "64", + not(target_arch = "mips64"), + not(target_arch = "mips64r6"), + ))] + unsafe { + let mut result = MaybeUninit::::uninit(); + ret(syscall!( + __NR_newfstatat, + raw_fd(AT_FDCWD), + path, + &mut result, + c_uint(0) + ))?; + Ok(result.assume_init()) + } +} + +#[cfg(any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" +))] +fn stat_old(path: &CStr) -> io::Result { + let mut result = MaybeUninit::::uninit(); + + #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] + unsafe { + ret(syscall!( + __NR_newfstatat, + raw_fd(AT_FDCWD), + path, + &mut result, + c_uint(0) + ))?; + stat_to_stat(result.assume_init()) + } + + #[cfg(target_pointer_width = "32")] + unsafe { + ret(syscall!( + __NR_fstatat64, + raw_fd(AT_FDCWD), + path, + &mut result, + c_uint(0) + ))?; + stat_to_stat(result.assume_init()) + } +} + +#[inline] +pub(crate) fn statat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result { + // See the comments in `fstat` about using `crate::fs::statx` here. + #[cfg(any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" + ))] + { + match crate::fs::statx(dirfd, path, flags, StatxFlags::BASIC_STATS) { + Ok(x) => statx_to_stat(x), + Err(io::Errno::NOSYS) => statat_old(dirfd, path, flags), + Err(err) => Err(err), + } + } + + #[cfg(all( + target_pointer_width = "64", + not(target_arch = "mips64"), + not(target_arch = "mips64r6"), + ))] + unsafe { + let mut result = MaybeUninit::::uninit(); + ret(syscall!(__NR_newfstatat, dirfd, path, &mut result, flags))?; + Ok(result.assume_init()) + } +} + +#[cfg(any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" +))] +fn statat_old(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result { + let mut result = MaybeUninit::::uninit(); + + #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] + unsafe { + ret(syscall!(__NR_newfstatat, dirfd, path, &mut result, flags))?; + stat_to_stat(result.assume_init()) + } + + #[cfg(target_pointer_width = "32")] + unsafe { + ret(syscall!(__NR_fstatat64, dirfd, path, &mut result, flags))?; + stat_to_stat(result.assume_init()) + } +} + +#[inline] +pub(crate) fn lstat(path: &CStr) -> io::Result { + // See the comments in `fstat` about using `crate::fs::statx` here. + #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))] + { + match crate::fs::statx( + crate::fs::CWD, + path, + AtFlags::SYMLINK_NOFOLLOW, + StatxFlags::BASIC_STATS, + ) { + Ok(x) => statx_to_stat(x), + Err(io::Errno::NOSYS) => lstat_old(path), + Err(err) => Err(err), + } + } + + #[cfg(all(target_pointer_width = "64", not(target_arch = "mips64")))] + unsafe { + let mut result = MaybeUninit::::uninit(); + ret(syscall!( + __NR_newfstatat, + raw_fd(AT_FDCWD), + path, + &mut result, + c_uint(AT_SYMLINK_NOFOLLOW) + ))?; + Ok(result.assume_init()) + } +} + +#[cfg(any(target_pointer_width = "32", target_arch = "mips64"))] +fn lstat_old(path: &CStr) -> io::Result { + let mut result = MaybeUninit::::uninit(); + + #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] + unsafe { + ret(syscall!( + __NR_newfstatat, + raw_fd(AT_FDCWD), + path, + &mut result, + c_uint(AT_SYMLINK_NOFOLLOW) + ))?; + stat_to_stat(result.assume_init()) + } + + #[cfg(target_pointer_width = "32")] + unsafe { + ret(syscall!( + __NR_fstatat64, + raw_fd(AT_FDCWD), + path, + &mut result, + c_uint(AT_SYMLINK_NOFOLLOW) + ))?; + stat_to_stat(result.assume_init()) + } +} + +/// Convert from a Linux `statx` value to rustix's `Stat`. +#[cfg(any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" +))] +fn statx_to_stat(x: crate::fs::Statx) -> io::Result { + Ok(Stat { + st_dev: crate::fs::makedev(x.stx_dev_major, x.stx_dev_minor), + st_mode: x.stx_mode.into(), + st_nlink: x.stx_nlink.into(), + st_uid: x.stx_uid.into(), + st_gid: x.stx_gid.into(), + st_rdev: crate::fs::makedev(x.stx_rdev_major, x.stx_rdev_minor), + st_size: x.stx_size.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_blksize: x.stx_blksize.into(), + st_blocks: x.stx_blocks.into(), + st_atime: i64::from(x.stx_atime.tv_sec), + st_atime_nsec: x.stx_atime.tv_nsec.into(), + st_mtime: i64::from(x.stx_mtime.tv_sec), + st_mtime_nsec: x.stx_mtime.tv_nsec.into(), + st_ctime: i64::from(x.stx_ctime.tv_sec), + st_ctime_nsec: x.stx_ctime.tv_nsec.into(), + st_ino: x.stx_ino.into(), + }) +} + +/// Convert from a Linux `stat64` value to rustix's `Stat`. +#[cfg(target_pointer_width = "32")] +fn stat_to_stat(s64: linux_raw_sys::general::stat64) -> io::Result { + Ok(Stat { + st_dev: s64.st_dev.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_mode: s64.st_mode.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_nlink: s64.st_nlink.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_uid: s64.st_uid.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_gid: s64.st_gid.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_rdev: s64.st_rdev.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_size: s64.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_blksize: s64.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_blocks: s64.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_atime: i64::from(s64.st_atime.to_signed()), + st_atime_nsec: s64 + .st_atime_nsec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + st_mtime: i64::from(s64.st_mtime.to_signed()), + st_mtime_nsec: s64 + .st_mtime_nsec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + st_ctime: i64::from(s64.st_ctime.to_signed()), + st_ctime_nsec: s64 + .st_ctime_nsec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + st_ino: s64.st_ino.try_into().map_err(|_| io::Errno::OVERFLOW)?, + }) +} + +/// Convert from a Linux `stat` value to rustix's `Stat`. +#[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] +fn stat_to_stat(s: linux_raw_sys::general::stat) -> io::Result { + Ok(Stat { + st_dev: s.st_dev.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_mode: s.st_mode.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_nlink: s.st_nlink.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_uid: s.st_uid.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_gid: s.st_gid.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_rdev: s.st_rdev.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_size: s.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_blksize: s.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_blocks: s.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_atime: i64::from(s.st_atime.to_signed()), + st_atime_nsec: s + .st_atime_nsec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + st_mtime: i64::from(s.st_mtime.to_signed()), + st_mtime_nsec: s + .st_mtime_nsec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + st_ctime: i64::from(s.st_ctime.to_signed()), + st_ctime_nsec: s + .st_ctime_nsec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + st_ino: s.st_ino.try_into().map_err(|_| io::Errno::OVERFLOW)?, + }) +} + +#[inline] +pub(crate) fn statx( + dirfd: BorrowedFd<'_>, + path: &CStr, + flags: AtFlags, + mask: StatxFlags, +) -> io::Result { + // If a future Linux kernel adds more fields to `struct statx` and users + // passing flags unknown to rustix in `StatxFlags`, we could end up + // writing outside of the buffer. To prevent this possibility, we mask off + // any flags that we don't know about. + // + // This includes `STATX__RESERVED`, which has a value that we know, but + // which could take on arbitrary new meaning in the future. Linux currently + // rejects this flag with `EINVAL`, so we do the same. + // + // This doesn't rely on `STATX_ALL` because [it's deprecated] and already + // doesn't represent all the known flags. + // + // [it's deprecated]: https://patchwork.kernel.org/project/linux-fsdevel/patch/20200505095915.11275-7-mszeredi@redhat.com/ + if (mask.bits() & STATX__RESERVED) == STATX__RESERVED { + return Err(io::Errno::INVAL); + } + let mask = mask & StatxFlags::all(); + + unsafe { + let mut statx_buf = MaybeUninit::::uninit(); + ret(syscall!( + __NR_statx, + dirfd, + path, + flags, + mask, + &mut statx_buf + ))?; + Ok(statx_buf.assume_init()) + } +} + +#[cfg(not(feature = "linux_4_11"))] +#[inline] +pub(crate) fn is_statx_available() -> bool { + unsafe { + // Call `statx` with null pointers so that if it fails for any reason + // other than `EFAULT`, we know it's not supported. This can use + // "readonly" because we don't pass it a buffer to mutate. + matches!( + ret(syscall_readonly!( + __NR_statx, + raw_fd(AT_FDCWD), + zero(), + zero(), + zero(), + zero() + )), + Err(io::Errno::FAULT) + ) + } +} + +#[inline] +pub(crate) fn fstatfs(fd: BorrowedFd<'_>) -> io::Result { + #[cfg(target_pointer_width = "32")] + unsafe { + let mut result = MaybeUninit::::uninit(); + ret(syscall!( + __NR_fstatfs64, + fd, + size_of::(), + &mut result + ))?; + Ok(result.assume_init()) + } + + #[cfg(target_pointer_width = "64")] + unsafe { + let mut result = MaybeUninit::::uninit(); + ret(syscall!(__NR_fstatfs, fd, &mut result))?; + Ok(result.assume_init()) + } +} + +#[inline] +pub(crate) fn fstatvfs(fd: BorrowedFd<'_>) -> io::Result { + // Linux doesn't have an `fstatvfs` syscall; we have to do `fstatfs` and + // translate the fields as best we can. + let statfs = fstatfs(fd)?; + + Ok(statfs_to_statvfs(statfs)) +} + +#[inline] +pub(crate) fn statfs(path: &CStr) -> io::Result { + #[cfg(target_pointer_width = "32")] + unsafe { + let mut result = MaybeUninit::::uninit(); + ret(syscall!( + __NR_statfs64, + path, + size_of::(), + &mut result + ))?; + Ok(result.assume_init()) + } + #[cfg(target_pointer_width = "64")] + unsafe { + let mut result = MaybeUninit::::uninit(); + ret(syscall!(__NR_statfs, path, &mut result))?; + Ok(result.assume_init()) + } +} + +#[inline] +pub(crate) fn statvfs(path: &CStr) -> io::Result { + // Linux doesn't have a `statvfs` syscall; we have to do `statfs` and + // translate the fields as best we can. + let statfs = statfs(path)?; + + Ok(statfs_to_statvfs(statfs)) +} + +fn statfs_to_statvfs(statfs: StatFs) -> StatVfs { + let Fsid { val } = Fsid { + val: statfs.f_fsid.val, + }; + let [f_fsid_val0, f_fsid_val1]: [i32; 2] = val; + + StatVfs { + f_bsize: statfs.f_bsize as u64, + f_frsize: if statfs.f_frsize != 0 { + statfs.f_frsize + } else { + statfs.f_bsize + } as u64, + f_blocks: statfs.f_blocks as u64, + f_bfree: statfs.f_bfree as u64, + f_bavail: statfs.f_bavail as u64, + f_files: statfs.f_files as u64, + f_ffree: statfs.f_ffree as u64, + f_favail: statfs.f_ffree as u64, + f_fsid: u64::from(f_fsid_val0 as u32) | (u64::from(f_fsid_val1 as u32) << 32), + f_flag: StatVfsMountFlags::from_bits_retain(statfs.f_flags as u64), + f_namemax: statfs.f_namelen as u64, + } +} + +#[cfg(feature = "alloc")] +#[inline] +pub(crate) fn readlink(path: &CStr, buf: &mut [u8]) -> io::Result { + let (buf_addr_mut, buf_len) = slice_mut(buf); + unsafe { + ret_usize(syscall!( + __NR_readlinkat, + raw_fd(AT_FDCWD), + path, + buf_addr_mut, + buf_len + )) + } +} + +#[inline] +pub(crate) unsafe fn readlinkat( + dirfd: BorrowedFd<'_>, + path: &CStr, + buf: (*mut u8, usize), +) -> io::Result { + ret_usize(syscall!( + __NR_readlinkat, + dirfd, + path, + buf.0, + pass_usize(buf.1) + )) +} + +#[inline] +pub(crate) fn fcntl_getfl(fd: BorrowedFd<'_>) -> io::Result { + #[cfg(target_pointer_width = "32")] + unsafe { + ret_c_uint(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETFL))) + .map(OFlags::from_bits_retain) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret_c_uint(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETFL))).map(OFlags::from_bits_retain) + } +} + +#[inline] +pub(crate) fn fcntl_setfl(fd: BorrowedFd<'_>, flags: OFlags) -> io::Result<()> { + // Always enable support for large files. + let flags = flags | OFlags::from_bits_retain(c::O_LARGEFILE); + + #[cfg(target_pointer_width = "32")] + unsafe { + ret(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_SETFL), flags)) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret(syscall_readonly!(__NR_fcntl, fd, c_uint(F_SETFL), flags)) + } +} + +#[inline] +pub(crate) fn fcntl_get_seals(fd: BorrowedFd<'_>) -> io::Result { + #[cfg(target_pointer_width = "32")] + unsafe { + ret_c_int(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GET_SEALS))) + .map(|seals| SealFlags::from_bits_retain(seals as u32)) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret_c_int(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GET_SEALS))) + .map(|seals| SealFlags::from_bits_retain(seals as u32)) + } +} + +#[inline] +pub(crate) fn fcntl_add_seals(fd: BorrowedFd<'_>, seals: SealFlags) -> io::Result<()> { + #[cfg(target_pointer_width = "32")] + unsafe { + ret(syscall_readonly!( + __NR_fcntl64, + fd, + c_uint(F_ADD_SEALS), + seals + )) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret(syscall_readonly!( + __NR_fcntl, + fd, + c_uint(F_ADD_SEALS), + seals + )) + } +} + +#[inline] +pub(crate) fn fcntl_lock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> { + #[cfg(target_pointer_width = "64")] + use linux_raw_sys::general::{flock, F_SETLK, F_SETLKW}; + #[cfg(target_pointer_width = "32")] + use linux_raw_sys::general::{flock64 as flock, F_SETLK64 as F_SETLK, F_SETLKW64 as F_SETLKW}; + use linux_raw_sys::general::{F_RDLCK, F_UNLCK, F_WRLCK}; + + let (cmd, l_type) = match operation { + FlockOperation::LockShared => (F_SETLKW, F_RDLCK), + FlockOperation::LockExclusive => (F_SETLKW, F_WRLCK), + FlockOperation::Unlock => (F_SETLKW, F_UNLCK), + FlockOperation::NonBlockingLockShared => (F_SETLK, F_RDLCK), + FlockOperation::NonBlockingLockExclusive => (F_SETLK, F_WRLCK), + FlockOperation::NonBlockingUnlock => (F_SETLK, F_UNLCK), + }; + + let lock = flock { + l_type: l_type as _, + + // When `l_len` is zero, this locks all the bytes from + // `l_whence`/`l_start` to the end of the file, even as the + // file grows dynamically. + l_whence: SEEK_SET as _, + l_start: 0, + l_len: 0, + + // Unused. + l_pid: 0, + }; + + #[cfg(target_pointer_width = "32")] + unsafe { + ret(syscall_readonly!( + __NR_fcntl64, + fd, + c_uint(cmd), + by_ref(&lock) + )) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret(syscall_readonly!( + __NR_fcntl, + fd, + c_uint(cmd), + by_ref(&lock) + )) + } +} + +#[inline] +pub(crate) fn rename(old_path: &CStr, new_path: &CStr) -> io::Result<()> { + #[cfg(target_arch = "riscv64")] + unsafe { + ret(syscall_readonly!( + __NR_renameat2, + raw_fd(AT_FDCWD), + old_path, + raw_fd(AT_FDCWD), + new_path, + c_uint(0) + )) + } + #[cfg(not(target_arch = "riscv64"))] + unsafe { + ret(syscall_readonly!( + __NR_renameat, + raw_fd(AT_FDCWD), + old_path, + raw_fd(AT_FDCWD), + new_path + )) + } +} + +#[inline] +pub(crate) fn renameat( + old_dirfd: BorrowedFd<'_>, + old_path: &CStr, + new_dirfd: BorrowedFd<'_>, + new_path: &CStr, +) -> io::Result<()> { + #[cfg(target_arch = "riscv64")] + unsafe { + ret(syscall_readonly!( + __NR_renameat2, + old_dirfd, + old_path, + new_dirfd, + new_path, + c_uint(0) + )) + } + #[cfg(not(target_arch = "riscv64"))] + unsafe { + ret(syscall_readonly!( + __NR_renameat, + old_dirfd, + old_path, + new_dirfd, + new_path + )) + } +} + +#[inline] +pub(crate) fn renameat2( + old_dirfd: BorrowedFd<'_>, + old_path: &CStr, + new_dirfd: BorrowedFd<'_>, + new_path: &CStr, + flags: RenameFlags, +) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_renameat2, + old_dirfd, + old_path, + new_dirfd, + new_path, + flags + )) + } +} + +#[inline] +pub(crate) fn unlink(path: &CStr) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_unlinkat, + raw_fd(AT_FDCWD), + path, + c_uint(0) + )) + } +} + +#[inline] +pub(crate) fn unlinkat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_unlinkat, dirfd, path, flags)) } +} + +#[inline] +pub(crate) fn rmdir(path: &CStr) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_unlinkat, + raw_fd(AT_FDCWD), + path, + c_uint(AT_REMOVEDIR) + )) + } +} + +#[inline] +pub(crate) fn link(old_path: &CStr, new_path: &CStr) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_linkat, + raw_fd(AT_FDCWD), + old_path, + raw_fd(AT_FDCWD), + new_path, + c_uint(0) + )) + } +} + +#[inline] +pub(crate) fn linkat( + old_dirfd: BorrowedFd<'_>, + old_path: &CStr, + new_dirfd: BorrowedFd<'_>, + new_path: &CStr, + flags: AtFlags, +) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_linkat, + old_dirfd, + old_path, + new_dirfd, + new_path, + flags + )) + } +} + +#[inline] +pub(crate) fn symlink(old_path: &CStr, new_path: &CStr) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_symlinkat, + old_path, + raw_fd(AT_FDCWD), + new_path + )) + } +} + +#[inline] +pub(crate) fn symlinkat(old_path: &CStr, dirfd: BorrowedFd<'_>, new_path: &CStr) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_symlinkat, old_path, dirfd, new_path)) } +} + +#[inline] +pub(crate) fn mkdir(path: &CStr, mode: Mode) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_mkdirat, + raw_fd(AT_FDCWD), + path, + mode + )) + } +} + +#[inline] +pub(crate) fn mkdirat(dirfd: BorrowedFd<'_>, path: &CStr, mode: Mode) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_mkdirat, dirfd, path, mode)) } +} + +#[cfg(feature = "alloc")] +#[inline] +pub(crate) fn getdents(fd: BorrowedFd<'_>, dirent: &mut [u8]) -> io::Result { + let (dirent_addr_mut, dirent_len) = slice_mut(dirent); + + unsafe { ret_usize(syscall!(__NR_getdents64, fd, dirent_addr_mut, dirent_len)) } +} + +#[inline] +pub(crate) fn getdents_uninit( + fd: BorrowedFd<'_>, + dirent: &mut [MaybeUninit], +) -> io::Result { + let (dirent_addr_mut, dirent_len) = slice_mut(dirent); + + unsafe { ret_usize(syscall!(__NR_getdents64, fd, dirent_addr_mut, dirent_len)) } +} + +#[inline] +pub(crate) fn utimensat( + dirfd: BorrowedFd<'_>, + path: &CStr, + times: &Timestamps, + flags: AtFlags, +) -> io::Result<()> { + _utimensat(dirfd, Some(path), times, flags) +} + +#[inline] +fn _utimensat( + dirfd: BorrowedFd<'_>, + path: Option<&CStr>, + times: &Timestamps, + flags: AtFlags, +) -> io::Result<()> { + // `utimensat_time64` was introduced in Linux 5.1. The old `utimensat` + // syscall is not y2038-compatible on 32-bit architectures. + #[cfg(target_pointer_width = "32")] + unsafe { + match ret(syscall_readonly!( + __NR_utimensat_time64, + dirfd, + path, + by_ref(times), + flags + )) { + Err(io::Errno::NOSYS) => _utimensat_old(dirfd, path, times, flags), + otherwise => otherwise, + } + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret(syscall_readonly!( + __NR_utimensat, + dirfd, + path, + by_ref(times), + flags + )) + } +} + +#[cfg(target_pointer_width = "32")] +unsafe fn _utimensat_old( + dirfd: BorrowedFd<'_>, + path: Option<&CStr>, + times: &Timestamps, + flags: AtFlags, +) -> io::Result<()> { + // See the comments in `clock_gettime_via_syscall` about emulation. + let old_times = [ + __kernel_old_timespec { + tv_sec: times + .last_access + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: times + .last_access + .tv_nsec + .try_into() + .map_err(|_| io::Errno::INVAL)?, + }, + __kernel_old_timespec { + tv_sec: times + .last_modification + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: times + .last_modification + .tv_nsec + .try_into() + .map_err(|_| io::Errno::INVAL)?, + }, + ]; + // The length of the array is fixed and not passed into the syscall. + let old_times_addr = slice_just_addr(&old_times); + ret(syscall_readonly!( + __NR_utimensat, + dirfd, + path, + old_times_addr, + flags + )) +} + +#[inline] +pub(crate) fn futimens(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()> { + _utimensat(fd, None, times, AtFlags::empty()) +} + +#[inline] +pub(crate) fn access(path: &CStr, access: Access) -> io::Result<()> { + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + { + accessat_noflags(CWD, path, access) + } + + #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] + unsafe { + ret(syscall_readonly!(__NR_access, path, access)) + } +} + +pub(crate) fn accessat( + dirfd: BorrowedFd<'_>, + path: &CStr, + access: Access, + flags: AtFlags, +) -> io::Result<()> { + if !flags + .difference(AtFlags::EACCESS | AtFlags::SYMLINK_NOFOLLOW) + .is_empty() + { + return Err(io::Errno::INVAL); + } + + // Linux's `faccessat` syscall doesn't have a flags argument, so if we have + // any flags, use the newer `faccessat2` introduced in Linux 5.8 which + // does. Unless we're on Android where using newer system calls can cause + // seccomp to abort the process. + #[cfg(not(target_os = "android"))] + if !flags.is_empty() { + unsafe { + match ret(syscall_readonly!( + __NR_faccessat2, + dirfd, + path, + access, + flags + )) { + Ok(()) => return Ok(()), + Err(io::Errno::NOSYS) => {} + Err(other) => return Err(other), + } + } + } + + // Linux's `faccessat` doesn't have a flags parameter. If we have + // `AT_EACCESS` and we're not setuid or setgid, we can emulate it. + if flags.is_empty() + || (flags.bits() == AT_EACCESS + && crate::backend::ugid::syscalls::getuid() + == crate::backend::ugid::syscalls::geteuid() + && crate::backend::ugid::syscalls::getgid() + == crate::backend::ugid::syscalls::getegid()) + { + return accessat_noflags(dirfd, path, access); + } + + Err(io::Errno::NOSYS) +} + +#[inline] +fn accessat_noflags(dirfd: BorrowedFd<'_>, path: &CStr, access: Access) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_faccessat, dirfd, path, access)) } +} + +#[inline] +pub(crate) fn copy_file_range( + fd_in: BorrowedFd<'_>, + off_in: Option<&mut u64>, + fd_out: BorrowedFd<'_>, + off_out: Option<&mut u64>, + len: usize, +) -> io::Result { + unsafe { + ret_usize(syscall!( + __NR_copy_file_range, + fd_in, + opt_mut(off_in), + fd_out, + opt_mut(off_out), + pass_usize(len), + c_uint(0) + )) + } +} + +#[inline] +pub(crate) fn memfd_create(name: &CStr, flags: MemfdFlags) -> io::Result { + unsafe { ret_owned_fd(syscall_readonly!(__NR_memfd_create, name, flags)) } +} + +#[inline] +pub(crate) fn sendfile( + out_fd: BorrowedFd<'_>, + in_fd: BorrowedFd<'_>, + offset: Option<&mut u64>, + count: usize, +) -> io::Result { + #[cfg(target_pointer_width = "32")] + unsafe { + ret_usize(syscall!( + __NR_sendfile64, + out_fd, + in_fd, + opt_mut(offset), + pass_usize(count) + )) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret_usize(syscall!( + __NR_sendfile, + out_fd, + in_fd, + opt_mut(offset), + pass_usize(count) + )) + } +} + +#[inline] +pub(crate) fn inotify_init1(flags: inotify::CreateFlags) -> io::Result { + unsafe { ret_owned_fd(syscall_readonly!(__NR_inotify_init1, flags)) } +} + +#[inline] +pub(crate) fn inotify_add_watch( + infd: BorrowedFd<'_>, + path: &CStr, + flags: inotify::WatchFlags, +) -> io::Result { + unsafe { ret_c_int(syscall_readonly!(__NR_inotify_add_watch, infd, path, flags)) } +} + +#[inline] +pub(crate) fn inotify_rm_watch(infd: BorrowedFd<'_>, wfd: i32) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_inotify_rm_watch, infd, c_int(wfd))) } +} + +#[inline] +pub(crate) unsafe fn getxattr( + path: &CStr, + name: &CStr, + value: (*mut u8, usize), +) -> io::Result { + ret_usize(syscall!( + __NR_getxattr, + path, + name, + value.0, + pass_usize(value.1) + )) +} + +#[inline] +pub(crate) unsafe fn lgetxattr( + path: &CStr, + name: &CStr, + value: (*mut u8, usize), +) -> io::Result { + ret_usize(syscall!( + __NR_lgetxattr, + path, + name, + value.0, + pass_usize(value.1) + )) +} + +#[inline] +pub(crate) unsafe fn fgetxattr( + fd: BorrowedFd<'_>, + name: &CStr, + value: (*mut u8, usize), +) -> io::Result { + ret_usize(syscall!( + __NR_fgetxattr, + fd, + name, + value.0, + pass_usize(value.1) + )) +} + +#[inline] +pub(crate) fn setxattr( + path: &CStr, + name: &CStr, + value: &[u8], + flags: XattrFlags, +) -> io::Result<()> { + let (value_addr, value_len) = slice(value); + unsafe { + ret(syscall_readonly!( + __NR_setxattr, + path, + name, + value_addr, + value_len, + flags + )) + } +} + +#[inline] +pub(crate) fn lsetxattr( + path: &CStr, + name: &CStr, + value: &[u8], + flags: XattrFlags, +) -> io::Result<()> { + let (value_addr, value_len) = slice(value); + unsafe { + ret(syscall_readonly!( + __NR_lsetxattr, + path, + name, + value_addr, + value_len, + flags + )) + } +} + +#[inline] +pub(crate) fn fsetxattr( + fd: BorrowedFd<'_>, + name: &CStr, + value: &[u8], + flags: XattrFlags, +) -> io::Result<()> { + let (value_addr, value_len) = slice(value); + unsafe { + ret(syscall_readonly!( + __NR_fsetxattr, + fd, + name, + value_addr, + value_len, + flags + )) + } +} + +#[inline] +pub(crate) unsafe fn listxattr(path: &CStr, list: (*mut u8, usize)) -> io::Result { + ret_usize(syscall!(__NR_listxattr, path, list.0, pass_usize(list.1))) +} + +#[inline] +pub(crate) unsafe fn llistxattr(path: &CStr, list: (*mut u8, usize)) -> io::Result { + ret_usize(syscall!(__NR_llistxattr, path, list.0, pass_usize(list.1))) +} + +#[inline] +pub(crate) unsafe fn flistxattr(fd: BorrowedFd<'_>, list: (*mut u8, usize)) -> io::Result { + ret_usize(syscall!(__NR_flistxattr, fd, list.0, pass_usize(list.1))) +} + +#[inline] +pub(crate) fn removexattr(path: &CStr, name: &CStr) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_removexattr, path, name)) } +} + +#[inline] +pub(crate) fn lremovexattr(path: &CStr, name: &CStr) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_lremovexattr, path, name)) } +} + +#[inline] +pub(crate) fn fremovexattr(fd: BorrowedFd<'_>, name: &CStr) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_fremovexattr, fd, name)) } +} + +// Some linux_raw_sys structs have unsigned types for values which are +// interpreted as signed. This defines a utility or casting to the +// same-sized signed type. +#[cfg(any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" +))] +mod to_signed { + pub(super) trait ToSigned { + type Signed; + fn to_signed(self) -> Self::Signed; + } + impl ToSigned for u32 { + type Signed = i32; + + fn to_signed(self) -> Self::Signed { + self as _ + } + } + impl ToSigned for i32 { + type Signed = i32; + + fn to_signed(self) -> Self::Signed { + self + } + } + impl ToSigned for u64 { + type Signed = i64; + + fn to_signed(self) -> Self::Signed { + self as _ + } + } + impl ToSigned for i64 { + type Signed = i64; + + fn to_signed(self) -> Self::Signed { + self + } + } +} +#[cfg(any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" +))] +use to_signed::*; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_sizes() { + assert_eq_size!(linux_raw_sys::general::__kernel_loff_t, u64); + assert_eq_align!(linux_raw_sys::general::__kernel_loff_t, u64); + + // Assert that `Timestamps` has the expected layout. + assert_eq_size!([linux_raw_sys::general::__kernel_timespec; 2], Timestamps); + assert_eq_align!([linux_raw_sys::general::__kernel_timespec; 2], Timestamps); + } +} diff --git a/vendor/rustix/src/backend/linux_raw/fs/types.rs b/vendor/rustix/src/backend/linux_raw/fs/types.rs new file mode 100644 index 00000000..ce6461c3 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/fs/types.rs @@ -0,0 +1,848 @@ +use crate::ffi; +use bitflags::bitflags; + +bitflags! { + /// `*_OK` constants for use with [`accessat`]. + /// + /// [`accessat`]: fn.accessat.html + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct Access: ffi::c_uint { + /// `R_OK` + const READ_OK = linux_raw_sys::general::R_OK; + + /// `W_OK` + const WRITE_OK = linux_raw_sys::general::W_OK; + + /// `X_OK` + const EXEC_OK = linux_raw_sys::general::X_OK; + + /// `F_OK` + const EXISTS = linux_raw_sys::general::F_OK; + + /// + const _ = !0; + } +} + +bitflags! { + /// `AT_*` constants for use with [`openat`], [`statat`], and other `*at` + /// functions. + /// + /// [`openat`]: crate::fs::openat + /// [`statat`]: crate::fs::statat + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct AtFlags: ffi::c_uint { + /// `AT_SYMLINK_NOFOLLOW` + const SYMLINK_NOFOLLOW = linux_raw_sys::general::AT_SYMLINK_NOFOLLOW; + + /// `AT_EACCESS` + const EACCESS = linux_raw_sys::general::AT_EACCESS; + + /// `AT_REMOVEDIR` + const REMOVEDIR = linux_raw_sys::general::AT_REMOVEDIR; + + /// `AT_SYMLINK_FOLLOW` + const SYMLINK_FOLLOW = linux_raw_sys::general::AT_SYMLINK_FOLLOW; + + /// `AT_NO_AUTOMOUNT` + const NO_AUTOMOUNT = linux_raw_sys::general::AT_NO_AUTOMOUNT; + + /// `AT_EMPTY_PATH` + const EMPTY_PATH = linux_raw_sys::general::AT_EMPTY_PATH; + + /// `AT_STATX_SYNC_AS_STAT` + const STATX_SYNC_AS_STAT = linux_raw_sys::general::AT_STATX_SYNC_AS_STAT; + + /// `AT_STATX_FORCE_SYNC` + const STATX_FORCE_SYNC = linux_raw_sys::general::AT_STATX_FORCE_SYNC; + + /// `AT_STATX_DONT_SYNC` + const STATX_DONT_SYNC = linux_raw_sys::general::AT_STATX_DONT_SYNC; + + /// + const _ = !0; + } +} + +bitflags! { + /// `S_I*` constants for use with [`openat`], [`chmodat`], and [`fchmod`]. + /// + /// [`openat`]: crate::fs::openat + /// [`chmodat`]: crate::fs::chmodat + /// [`fchmod`]: crate::fs::fchmod + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct Mode: RawMode { + /// `S_IRWXU` + const RWXU = linux_raw_sys::general::S_IRWXU; + + /// `S_IRUSR` + const RUSR = linux_raw_sys::general::S_IRUSR; + + /// `S_IWUSR` + const WUSR = linux_raw_sys::general::S_IWUSR; + + /// `S_IXUSR` + const XUSR = linux_raw_sys::general::S_IXUSR; + + /// `S_IRWXG` + const RWXG = linux_raw_sys::general::S_IRWXG; + + /// `S_IRGRP` + const RGRP = linux_raw_sys::general::S_IRGRP; + + /// `S_IWGRP` + const WGRP = linux_raw_sys::general::S_IWGRP; + + /// `S_IXGRP` + const XGRP = linux_raw_sys::general::S_IXGRP; + + /// `S_IRWXO` + const RWXO = linux_raw_sys::general::S_IRWXO; + + /// `S_IROTH` + const ROTH = linux_raw_sys::general::S_IROTH; + + /// `S_IWOTH` + const WOTH = linux_raw_sys::general::S_IWOTH; + + /// `S_IXOTH` + const XOTH = linux_raw_sys::general::S_IXOTH; + + /// `S_ISUID` + const SUID = linux_raw_sys::general::S_ISUID; + + /// `S_ISGID` + const SGID = linux_raw_sys::general::S_ISGID; + + /// `S_ISVTX` + const SVTX = linux_raw_sys::general::S_ISVTX; + + /// + const _ = !0; + } +} + +impl Mode { + /// Construct a `Mode` from the mode bits of the `st_mode` field of a + /// `Mode`. + #[inline] + pub const fn from_raw_mode(st_mode: RawMode) -> Self { + Self::from_bits_truncate(st_mode & !linux_raw_sys::general::S_IFMT) + } + + /// Construct an `st_mode` value from a `Mode`. + #[inline] + pub const fn as_raw_mode(self) -> RawMode { + self.bits() + } +} + +impl From for Mode { + /// Support conversions from raw mode values to `Mode`. + /// + /// ``` + /// use rustix::fs::{Mode, RawMode}; + /// assert_eq!(Mode::from(0o700), Mode::RWXU); + /// ``` + #[inline] + fn from(st_mode: RawMode) -> Self { + Self::from_raw_mode(st_mode) + } +} + +impl From for RawMode { + /// Support conversions from `Mode` to raw mode values. + /// + /// ``` + /// use rustix::fs::{Mode, RawMode}; + /// assert_eq!(RawMode::from(Mode::RWXU), 0o700); + /// ``` + #[inline] + fn from(mode: Mode) -> Self { + mode.as_raw_mode() + } +} + +bitflags! { + /// `O_*` constants for use with [`openat`]. + /// + /// [`openat`]: crate::fs::openat + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct OFlags: ffi::c_uint { + /// `O_ACCMODE` + const ACCMODE = linux_raw_sys::general::O_ACCMODE; + + /// Similar to `ACCMODE`, but just includes the read/write flags, and + /// no other flags. + /// + /// On some platforms, `PATH` may be included in `ACCMODE`, when + /// sometimes we really just want the read/write bits. Caution is + /// indicated, as the presence of `PATH` may mean that the read/write + /// bits don't have their usual meaning. + const RWMODE = linux_raw_sys::general::O_RDONLY | + linux_raw_sys::general::O_WRONLY | + linux_raw_sys::general::O_RDWR; + + /// `O_APPEND` + const APPEND = linux_raw_sys::general::O_APPEND; + + /// `O_CREAT` + #[doc(alias = "CREAT")] + const CREATE = linux_raw_sys::general::O_CREAT; + + /// `O_DIRECTORY` + const DIRECTORY = linux_raw_sys::general::O_DIRECTORY; + + /// `O_DSYNC` + const DSYNC = linux_raw_sys::general::O_SYNC; + + /// `O_EXCL` + const EXCL = linux_raw_sys::general::O_EXCL; + + /// `O_FSYNC` + const FSYNC = linux_raw_sys::general::O_SYNC; + + /// `O_NOFOLLOW` + const NOFOLLOW = linux_raw_sys::general::O_NOFOLLOW; + + /// `O_NONBLOCK` + const NONBLOCK = linux_raw_sys::general::O_NONBLOCK; + + /// `O_RDONLY` + const RDONLY = linux_raw_sys::general::O_RDONLY; + + /// `O_WRONLY` + const WRONLY = linux_raw_sys::general::O_WRONLY; + + /// `O_RDWR` + /// + /// This is not equal to `RDONLY | WRONLY`. It's a distinct flag. + const RDWR = linux_raw_sys::general::O_RDWR; + + /// `O_NOCTTY` + const NOCTTY = linux_raw_sys::general::O_NOCTTY; + + /// `O_RSYNC` + const RSYNC = linux_raw_sys::general::O_SYNC; + + /// `O_SYNC` + const SYNC = linux_raw_sys::general::O_SYNC; + + /// `O_TRUNC` + const TRUNC = linux_raw_sys::general::O_TRUNC; + + /// `O_PATH` + const PATH = linux_raw_sys::general::O_PATH; + + /// `O_CLOEXEC` + const CLOEXEC = linux_raw_sys::general::O_CLOEXEC; + + /// `O_TMPFILE` + const TMPFILE = linux_raw_sys::general::O_TMPFILE; + + /// `O_NOATIME` + const NOATIME = linux_raw_sys::general::O_NOATIME; + + /// `O_DIRECT` + const DIRECT = linux_raw_sys::general::O_DIRECT; + + /// `O_LARGEFILE` + /// + /// Rustix and/or libc will automatically set this flag when + /// appropriate in the [`rustix::fs::open`] family of functions, so + /// typical users do not need to care about it. It may be reported in + /// the return of `fcntl_getfl`, though. + const LARGEFILE = linux_raw_sys::general::O_LARGEFILE; + + /// `O_ASYNC`, `FASYNC` + /// + /// This flag can't be used with the [`rustix::fs::open`] family of + /// functions, use [`rustix::fs::fcntl_setfl`] instead. + const ASYNC = linux_raw_sys::general::FASYNC; + + /// + const _ = !0; + } +} + +bitflags! { + /// `RESOLVE_*` constants for use with [`openat2`]. + /// + /// [`openat2`]: crate::fs::openat2 + #[repr(transparent)] + #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct ResolveFlags: u64 { + /// `RESOLVE_NO_XDEV` + const NO_XDEV = linux_raw_sys::general::RESOLVE_NO_XDEV as u64; + + /// `RESOLVE_NO_MAGICLINKS` + const NO_MAGICLINKS = linux_raw_sys::general::RESOLVE_NO_MAGICLINKS as u64; + + /// `RESOLVE_NO_SYMLINKS` + const NO_SYMLINKS = linux_raw_sys::general::RESOLVE_NO_SYMLINKS as u64; + + /// `RESOLVE_BENEATH` + const BENEATH = linux_raw_sys::general::RESOLVE_BENEATH as u64; + + /// `RESOLVE_IN_ROOT` + const IN_ROOT = linux_raw_sys::general::RESOLVE_IN_ROOT as u64; + + /// `RESOLVE_CACHED` (since Linux 5.12) + const CACHED = linux_raw_sys::general::RESOLVE_CACHED as u64; + + /// + const _ = !0; + } +} + +bitflags! { + /// `RENAME_*` constants for use with [`renameat_with`]. + /// + /// [`renameat_with`]: crate::fs::renameat_with + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct RenameFlags: ffi::c_uint { + /// `RENAME_EXCHANGE` + const EXCHANGE = linux_raw_sys::general::RENAME_EXCHANGE; + + /// `RENAME_NOREPLACE` + const NOREPLACE = linux_raw_sys::general::RENAME_NOREPLACE; + + /// `RENAME_WHITEOUT` + const WHITEOUT = linux_raw_sys::general::RENAME_WHITEOUT; + + /// + const _ = !0; + } +} + +/// `S_IF*` constants for use with [`mknodat`] and [`Stat`]'s `st_mode` field. +/// +/// [`mknodat`]: crate::fs::mknodat +/// [`Stat`]: crate::fs::Stat +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum FileType { + /// `S_IFREG` + RegularFile = linux_raw_sys::general::S_IFREG as isize, + + /// `S_IFDIR` + Directory = linux_raw_sys::general::S_IFDIR as isize, + + /// `S_IFLNK` + Symlink = linux_raw_sys::general::S_IFLNK as isize, + + /// `S_IFIFO` + #[doc(alias = "IFO")] + Fifo = linux_raw_sys::general::S_IFIFO as isize, + + /// `S_IFSOCK` + Socket = linux_raw_sys::general::S_IFSOCK as isize, + + /// `S_IFCHR` + CharacterDevice = linux_raw_sys::general::S_IFCHR as isize, + + /// `S_IFBLK` + BlockDevice = linux_raw_sys::general::S_IFBLK as isize, + + /// An unknown filesystem object. + Unknown, +} + +impl FileType { + /// Construct a `FileType` from the `S_IFMT` bits of the `st_mode` field of + /// a `Stat`. + #[inline] + pub const fn from_raw_mode(st_mode: RawMode) -> Self { + match st_mode & linux_raw_sys::general::S_IFMT { + linux_raw_sys::general::S_IFREG => Self::RegularFile, + linux_raw_sys::general::S_IFDIR => Self::Directory, + linux_raw_sys::general::S_IFLNK => Self::Symlink, + linux_raw_sys::general::S_IFIFO => Self::Fifo, + linux_raw_sys::general::S_IFSOCK => Self::Socket, + linux_raw_sys::general::S_IFCHR => Self::CharacterDevice, + linux_raw_sys::general::S_IFBLK => Self::BlockDevice, + _ => Self::Unknown, + } + } + + /// Construct an `st_mode` value from a `FileType`. + #[inline] + pub const fn as_raw_mode(self) -> RawMode { + match self { + Self::RegularFile => linux_raw_sys::general::S_IFREG, + Self::Directory => linux_raw_sys::general::S_IFDIR, + Self::Symlink => linux_raw_sys::general::S_IFLNK, + Self::Fifo => linux_raw_sys::general::S_IFIFO, + Self::Socket => linux_raw_sys::general::S_IFSOCK, + Self::CharacterDevice => linux_raw_sys::general::S_IFCHR, + Self::BlockDevice => linux_raw_sys::general::S_IFBLK, + Self::Unknown => linux_raw_sys::general::S_IFMT, + } + } + + /// Construct a `FileType` from the `d_type` field of a `c::dirent`. + #[inline] + pub(crate) const fn from_dirent_d_type(d_type: u8) -> Self { + match d_type as u32 { + linux_raw_sys::general::DT_REG => Self::RegularFile, + linux_raw_sys::general::DT_DIR => Self::Directory, + linux_raw_sys::general::DT_LNK => Self::Symlink, + linux_raw_sys::general::DT_SOCK => Self::Socket, + linux_raw_sys::general::DT_FIFO => Self::Fifo, + linux_raw_sys::general::DT_CHR => Self::CharacterDevice, + linux_raw_sys::general::DT_BLK => Self::BlockDevice, + // linux_raw_sys::general::DT_UNKNOWN | + _ => Self::Unknown, + } + } +} + +/// `POSIX_FADV_*` constants for use with [`fadvise`]. +/// +/// [`fadvise`]: crate::fs::fadvise +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +pub enum Advice { + /// `POSIX_FADV_NORMAL` + Normal = linux_raw_sys::general::POSIX_FADV_NORMAL, + + /// `POSIX_FADV_SEQUENTIAL` + Sequential = linux_raw_sys::general::POSIX_FADV_SEQUENTIAL, + + /// `POSIX_FADV_RANDOM` + Random = linux_raw_sys::general::POSIX_FADV_RANDOM, + + /// `POSIX_FADV_NOREUSE` + NoReuse = linux_raw_sys::general::POSIX_FADV_NOREUSE, + + /// `POSIX_FADV_WILLNEED` + WillNeed = linux_raw_sys::general::POSIX_FADV_WILLNEED, + + /// `POSIX_FADV_DONTNEED` + DontNeed = linux_raw_sys::general::POSIX_FADV_DONTNEED, +} + +bitflags! { + /// `MFD_*` constants for use with [`memfd_create`]. + /// + /// [`memfd_create`]: crate::fs::memfd_create + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct MemfdFlags: ffi::c_uint { + /// `MFD_CLOEXEC` + const CLOEXEC = linux_raw_sys::general::MFD_CLOEXEC; + + /// `MFD_ALLOW_SEALING` + const ALLOW_SEALING = linux_raw_sys::general::MFD_ALLOW_SEALING; + + /// `MFD_HUGETLB` (since Linux 4.14) + const HUGETLB = linux_raw_sys::general::MFD_HUGETLB; + + /// `MFD_NOEXEC_SEAL` (since Linux 6.3) + const NOEXEC_SEAL = linux_raw_sys::general::MFD_NOEXEC_SEAL; + /// `MFD_EXEC` (since Linux 6.3) + const EXEC = linux_raw_sys::general::MFD_EXEC; + + /// `MFD_HUGE_64KB` + const HUGE_64KB = linux_raw_sys::general::MFD_HUGE_64KB; + /// `MFD_HUGE_512KB` + const HUGE_512KB = linux_raw_sys::general::MFD_HUGE_512KB; + /// `MFD_HUGE_1MB` + const HUGE_1MB = linux_raw_sys::general::MFD_HUGE_1MB; + /// `MFD_HUGE_2MB` + const HUGE_2MB = linux_raw_sys::general::MFD_HUGE_2MB; + /// `MFD_HUGE_8MB` + const HUGE_8MB = linux_raw_sys::general::MFD_HUGE_8MB; + /// `MFD_HUGE_16MB` + const HUGE_16MB = linux_raw_sys::general::MFD_HUGE_16MB; + /// `MFD_HUGE_32MB` + const HUGE_32MB = linux_raw_sys::general::MFD_HUGE_32MB; + /// `MFD_HUGE_256MB` + const HUGE_256MB = linux_raw_sys::general::MFD_HUGE_256MB; + /// `MFD_HUGE_512MB` + const HUGE_512MB = linux_raw_sys::general::MFD_HUGE_512MB; + /// `MFD_HUGE_1GB` + const HUGE_1GB = linux_raw_sys::general::MFD_HUGE_1GB; + /// `MFD_HUGE_2GB` + const HUGE_2GB = linux_raw_sys::general::MFD_HUGE_2GB; + /// `MFD_HUGE_16GB` + const HUGE_16GB = linux_raw_sys::general::MFD_HUGE_16GB; + + /// + const _ = !0; + } +} + +bitflags! { + /// `F_SEAL_*` constants for use with [`fcntl_add_seals`] and + /// [`fcntl_get_seals`]. + /// + /// [`fcntl_add_seals`]: crate::fs::fcntl_add_seals + /// [`fcntl_get_seals`]: crate::fs::fcntl_get_seals + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct SealFlags: u32 { + /// `F_SEAL_SEAL` + const SEAL = linux_raw_sys::general::F_SEAL_SEAL; + /// `F_SEAL_SHRINK` + const SHRINK = linux_raw_sys::general::F_SEAL_SHRINK; + /// `F_SEAL_GROW` + const GROW = linux_raw_sys::general::F_SEAL_GROW; + /// `F_SEAL_WRITE` + const WRITE = linux_raw_sys::general::F_SEAL_WRITE; + /// `F_SEAL_FUTURE_WRITE` (since Linux 5.1) + const FUTURE_WRITE = linux_raw_sys::general::F_SEAL_FUTURE_WRITE; + + /// + const _ = !0; + } +} + +bitflags! { + /// `FALLOC_FL_*` constants for use with [`fallocate`]. + /// + /// [`fallocate`]: crate::fs::fallocate + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct FallocateFlags: u32 { + /// `FALLOC_FL_KEEP_SIZE` + const KEEP_SIZE = linux_raw_sys::general::FALLOC_FL_KEEP_SIZE; + /// `FALLOC_FL_PUNCH_HOLE` + const PUNCH_HOLE = linux_raw_sys::general::FALLOC_FL_PUNCH_HOLE; + /// `FALLOC_FL_NO_HIDE_STALE` + const NO_HIDE_STALE = linux_raw_sys::general::FALLOC_FL_NO_HIDE_STALE; + /// `FALLOC_FL_COLLAPSE_RANGE` + const COLLAPSE_RANGE = linux_raw_sys::general::FALLOC_FL_COLLAPSE_RANGE; + /// `FALLOC_FL_ZERO_RANGE` + const ZERO_RANGE = linux_raw_sys::general::FALLOC_FL_ZERO_RANGE; + /// `FALLOC_FL_INSERT_RANGE` + const INSERT_RANGE = linux_raw_sys::general::FALLOC_FL_INSERT_RANGE; + /// `FALLOC_FL_UNSHARE_RANGE` + const UNSHARE_RANGE = linux_raw_sys::general::FALLOC_FL_UNSHARE_RANGE; + + /// + const _ = !0; + } +} + +bitflags! { + /// `ST_*` constants for use with [`StatVfs`]. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct StatVfsMountFlags: u64 { + /// `ST_MANDLOCK` + const MANDLOCK = linux_raw_sys::general::MS_MANDLOCK as u64; + + /// `ST_NOATIME` + const NOATIME = linux_raw_sys::general::MS_NOATIME as u64; + + /// `ST_NODEV` + const NODEV = linux_raw_sys::general::MS_NODEV as u64; + + /// `ST_NODIRATIME` + const NODIRATIME = linux_raw_sys::general::MS_NODIRATIME as u64; + + /// `ST_NOEXEC` + const NOEXEC = linux_raw_sys::general::MS_NOEXEC as u64; + + /// `ST_NOSUID` + const NOSUID = linux_raw_sys::general::MS_NOSUID as u64; + + /// `ST_RDONLY` + const RDONLY = linux_raw_sys::general::MS_RDONLY as u64; + + /// `ST_RELATIME` + const RELATIME = linux_raw_sys::general::MS_RELATIME as u64; + + /// `ST_SYNCHRONOUS` + const SYNCHRONOUS = linux_raw_sys::general::MS_SYNCHRONOUS as u64; + + /// + const _ = !0; + } +} + +/// `LOCK_*` constants for use with [`flock`] and [`fcntl_lock`]. +/// +/// [`flock`]: crate::fs::flock +/// [`fcntl_lock`]: crate::fs::fcntl_lock +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum FlockOperation { + /// `LOCK_SH` + LockShared = linux_raw_sys::general::LOCK_SH, + /// `LOCK_EX` + LockExclusive = linux_raw_sys::general::LOCK_EX, + /// `LOCK_UN` + Unlock = linux_raw_sys::general::LOCK_UN, + /// `LOCK_SH | LOCK_NB` + NonBlockingLockShared = linux_raw_sys::general::LOCK_SH | linux_raw_sys::general::LOCK_NB, + /// `LOCK_EX | LOCK_NB` + NonBlockingLockExclusive = linux_raw_sys::general::LOCK_EX | linux_raw_sys::general::LOCK_NB, + /// `LOCK_UN | LOCK_NB` + NonBlockingUnlock = linux_raw_sys::general::LOCK_UN | linux_raw_sys::general::LOCK_NB, +} + +/// `struct stat` for use with [`statat`] and [`fstat`]. +/// +/// [`statat`]: crate::fs::statat +/// [`fstat`]: crate::fs::fstat +// On 32-bit with `struct stat64` and mips64 with `struct stat`, Linux's +// `st_mtime` and friends are 32-bit, so we use our own struct, populated from +// `statx` where possible, to avoid the y2038 bug. +#[cfg(any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" +))] +#[derive(Debug, Copy, Clone)] +#[allow(missing_docs)] +#[non_exhaustive] +pub struct Stat { + pub st_dev: u64, + pub st_mode: u32, + pub st_nlink: u32, + pub st_uid: u32, + pub st_gid: u32, + pub st_rdev: u64, + pub st_size: i64, + pub st_blksize: u32, + pub st_blocks: u64, + pub st_atime: i64, + pub st_atime_nsec: u32, + pub st_mtime: i64, + pub st_mtime_nsec: u32, + pub st_ctime: i64, + pub st_ctime_nsec: u32, + pub st_ino: u64, +} + +/// `struct stat` for use with [`statat`] and [`fstat`]. +/// +/// [`statat`]: crate::fs::statat +/// [`fstat`]: crate::fs::fstat +#[repr(C)] +#[derive(Debug, Copy, Clone)] +#[allow(missing_docs)] +#[non_exhaustive] +#[cfg(target_arch = "x86_64")] +pub struct Stat { + pub st_dev: ffi::c_ulong, + pub st_ino: ffi::c_ulong, + pub st_nlink: ffi::c_ulong, + pub st_mode: ffi::c_uint, + pub st_uid: ffi::c_uint, + pub st_gid: ffi::c_uint, + pub(crate) __pad0: ffi::c_uint, + pub st_rdev: ffi::c_ulong, + pub st_size: ffi::c_long, + pub st_blksize: ffi::c_long, + pub st_blocks: ffi::c_long, + pub st_atime: ffi::c_long, + pub st_atime_nsec: ffi::c_ulong, + pub st_mtime: ffi::c_long, + pub st_mtime_nsec: ffi::c_ulong, + pub st_ctime: ffi::c_long, + pub st_ctime_nsec: ffi::c_ulong, + pub(crate) __unused: [ffi::c_long; 3], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +#[allow(missing_docs)] +#[non_exhaustive] +#[cfg(target_arch = "aarch64")] +pub struct Stat { + pub st_dev: ffi::c_ulong, + pub st_ino: ffi::c_ulong, + pub st_mode: ffi::c_uint, + pub st_nlink: ffi::c_uint, + pub st_uid: ffi::c_uint, + pub st_gid: ffi::c_uint, + pub st_rdev: ffi::c_ulong, + pub(crate) __pad1: ffi::c_ulong, + pub st_size: ffi::c_long, + pub st_blksize: ffi::c_int, + pub(crate) __pad2: ffi::c_int, + pub st_blocks: ffi::c_long, + pub st_atime: ffi::c_long, + pub st_atime_nsec: ffi::c_ulong, + pub st_mtime: ffi::c_long, + pub st_mtime_nsec: ffi::c_ulong, + pub st_ctime: ffi::c_long, + pub st_ctime_nsec: ffi::c_ulong, + pub(crate) __unused4: ffi::c_uint, + pub(crate) __unused5: ffi::c_uint, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +#[allow(missing_docs)] +#[non_exhaustive] +#[cfg(target_arch = "riscv64")] +pub struct Stat { + pub st_dev: ffi::c_ulong, + pub st_ino: ffi::c_ulong, + pub st_mode: ffi::c_uint, + pub st_nlink: ffi::c_uint, + pub st_uid: ffi::c_uint, + pub st_gid: ffi::c_uint, + pub st_rdev: ffi::c_ulong, + pub(crate) __pad1: ffi::c_ulong, + pub st_size: ffi::c_long, + pub st_blksize: ffi::c_int, + pub(crate) __pad2: ffi::c_int, + pub st_blocks: ffi::c_long, + pub st_atime: ffi::c_long, + pub st_atime_nsec: ffi::c_ulong, + pub st_mtime: ffi::c_long, + pub st_mtime_nsec: ffi::c_ulong, + pub st_ctime: ffi::c_long, + pub st_ctime_nsec: ffi::c_ulong, + pub(crate) __unused4: ffi::c_uint, + pub(crate) __unused5: ffi::c_uint, +} +// This follows `stat`. powerpc64 defines a `stat64` but it's not used. +#[repr(C)] +#[derive(Debug, Copy, Clone)] +#[allow(missing_docs)] +#[non_exhaustive] +#[cfg(target_arch = "powerpc64")] +pub struct Stat { + pub st_dev: ffi::c_ulong, + pub st_ino: ffi::c_ulong, + pub st_nlink: ffi::c_ulong, + pub st_mode: ffi::c_uint, + pub st_uid: ffi::c_uint, + pub st_gid: ffi::c_uint, + pub st_rdev: ffi::c_ulong, + pub st_size: ffi::c_long, + pub st_blksize: ffi::c_ulong, + pub st_blocks: ffi::c_ulong, + pub st_atime: ffi::c_long, + pub st_atime_nsec: ffi::c_ulong, + pub st_mtime: ffi::c_long, + pub st_mtime_nsec: ffi::c_ulong, + pub st_ctime: ffi::c_long, + pub st_ctime_nsec: ffi::c_ulong, + pub(crate) __unused4: ffi::c_ulong, + pub(crate) __unused5: ffi::c_ulong, + pub(crate) __unused6: ffi::c_ulong, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +#[allow(missing_docs)] +#[non_exhaustive] +#[cfg(target_arch = "s390x")] +pub struct Stat { + pub st_dev: ffi::c_ulong, + pub st_ino: ffi::c_ulong, + pub st_nlink: ffi::c_ulong, + pub st_mode: ffi::c_uint, + pub st_uid: ffi::c_uint, + pub st_gid: ffi::c_uint, + pub(crate) __pad1: ffi::c_uint, + pub st_rdev: ffi::c_ulong, + pub st_size: ffi::c_long, // Linux has `c_ulong` but we make it signed. + pub st_atime: ffi::c_long, + pub st_atime_nsec: ffi::c_ulong, + pub st_mtime: ffi::c_long, + pub st_mtime_nsec: ffi::c_ulong, + pub st_ctime: ffi::c_long, + pub st_ctime_nsec: ffi::c_ulong, + pub st_blksize: ffi::c_ulong, + pub st_blocks: ffi::c_long, + pub(crate) __unused: [ffi::c_ulong; 3], +} + +/// `struct statfs` for use with [`statfs`] and [`fstatfs`]. +/// +/// [`statfs`]: crate::fs::statfs +/// [`fstatfs`]: crate::fs::fstatfs +#[allow(clippy::module_name_repetitions)] +#[repr(C)] +#[cfg_attr(target_arch = "arm", repr(packed(4)))] +#[derive(Debug, Copy, Clone)] +#[allow(missing_docs)] +#[non_exhaustive] +pub struct StatFs { + pub f_type: FsWord, + #[cfg(not(any(target_arch = "arm", target_arch = "s390x")))] + pub f_bsize: ffi::c_long, + #[cfg(any(target_arch = "arm", target_arch = "s390x"))] + pub f_bsize: ffi::c_uint, + pub f_blocks: u64, + pub f_bfree: u64, + pub f_bavail: u64, + pub f_files: u64, + pub f_ffree: u64, + pub f_fsid: Fsid, + #[cfg(not(any(target_arch = "arm", target_arch = "s390x")))] + pub f_namelen: ffi::c_long, + #[cfg(any(target_arch = "arm", target_arch = "s390x"))] + pub f_namelen: ffi::c_uint, + #[cfg(not(any(target_arch = "arm", target_arch = "s390x")))] + pub f_frsize: ffi::c_long, + #[cfg(any(target_arch = "arm", target_arch = "s390x"))] + pub f_frsize: ffi::c_uint, + #[cfg(not(any(target_arch = "arm", target_arch = "s390x")))] + pub f_flags: ffi::c_long, + #[cfg(any(target_arch = "arm", target_arch = "s390x"))] + pub f_flags: ffi::c_uint, + #[cfg(not(target_arch = "s390x"))] + pub(crate) f_spare: [ffi::c_long; 4], + #[cfg(target_arch = "s390x")] + pub(crate) f_spare: [ffi::c_uint; 5], +} + +/// `fsid_t` for use with [`StatFs`]. +#[repr(C)] +#[derive(Debug, Copy, Clone)] +#[allow(missing_docs)] +pub struct Fsid { + pub(crate) val: [ffi::c_int; 2], +} + +/// `struct statvfs` for use with [`statvfs`] and [`fstatvfs`]. +/// +/// [`statvfs`]: crate::fs::statvfs +/// [`fstatvfs`]: crate::fs::fstatvfs +#[allow(missing_docs)] +pub struct StatVfs { + pub f_bsize: u64, + pub f_frsize: u64, + pub f_blocks: u64, + pub f_bfree: u64, + pub f_bavail: u64, + pub f_files: u64, + pub f_ffree: u64, + pub f_favail: u64, + pub f_fsid: u64, + pub f_flag: StatVfsMountFlags, + pub f_namemax: u64, +} + +/// `mode_t` +pub type RawMode = ffi::c_uint; + +/// `dev_t` +// Within the kernel the `dev_t` is 32-bit, but userspace uses a 64-bit field. +pub type Dev = u64; + +/// `__fsword_t` +#[cfg(not(any( + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "s390x" +)))] +pub type FsWord = ffi::c_long; + +/// `__fsword_t` +#[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] +pub type FsWord = i64; + +/// `__fsword_t` +#[cfg(target_arch = "s390x")] +pub type FsWord = u32; diff --git a/vendor/rustix/src/backend/linux_raw/io/errno.rs b/vendor/rustix/src/backend/linux_raw/io/errno.rs new file mode 100644 index 00000000..4ddf6df5 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/io/errno.rs @@ -0,0 +1,552 @@ +//! The `rustix` `Errno` type. +//! +//! This type holds an OS error code, which conceptually corresponds to an +//! `errno` value. +//! +//! # Safety +//! +//! Linux uses error codes in `-4095..0`; we use rustc attributes to describe +//! this restricted range of values. +#![allow(unsafe_code)] +#![cfg_attr(not(rustc_attrs), allow(unused_unsafe))] + +use crate::backend::c; +use crate::backend::fd::RawFd; +use crate::backend::reg::{RetNumber, RetReg}; +use crate::io; +use linux_raw_sys::errno; + +/// `errno`—An error code. +/// +/// The error type for `rustix` APIs. This is similar to [`std::io::Error`], +/// but only holds an OS error code, and no extra error value. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [Winsock] +/// - [FreeBSD] +/// - [NetBSD] +/// - [OpenBSD] +/// - [DragonFly BSD] +/// - [illumos] +/// - [glibc] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/errno.html +/// [Linux]: https://man7.org/linux/man-pages/man3/errno.3.html +/// [Winsock]: https://learn.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2 +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?errno +/// [NetBSD]: https://man.netbsd.org/errno.2 +/// [OpenBSD]: https://man.openbsd.org/errno.2 +/// [DragonFly BSD]: https://man.dragonflybsd.org/?command=errno§ion=2 +/// [illumos]: https://illumos.org/man/3C/errno +/// [glibc]: https://sourceware.org/glibc/manual/latest/html_node/Error-Codes.html +#[repr(transparent)] +#[doc(alias = "errno")] +#[derive(Eq, PartialEq, Hash, Copy, Clone)] +// Linux returns negated error codes, and we leave them in negated form, so +// error codes are in `-4095..0`. +#[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_start(0xf001))] +#[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_end(0xffff))] +pub struct Errno(u16); + +impl Errno { + /// Extract an `Errno` value from a `std::io::Error`. + /// + /// This isn't a `From` conversion because it's expected to be relatively + /// uncommon. + #[cfg(feature = "std")] + #[inline] + pub fn from_io_error(io_err: &std::io::Error) -> Option { + io_err.raw_os_error().and_then(|raw| { + // `std::io::Error` could theoretically have arbitrary OS error + // values, so check that they're in Linux's range. + if (1..4096).contains(&raw) { + Some(Self::from_errno(raw as u32)) + } else { + None + } + }) + } + + /// Extract the raw OS error number from this error. + #[inline] + pub const fn raw_os_error(self) -> i32 { + (self.0 as i16 as i32).wrapping_neg() + } + + /// Construct an `Errno` from a raw OS error number. + #[inline] + pub const fn from_raw_os_error(raw: i32) -> Self { + Self::from_errno(raw as u32) + } + + /// Convert from a C `errno` value (which is positive) to an `Errno`. + const fn from_errno(raw: u32) -> Self { + // We store error values in negated form, so that we don't have to + // negate them after every syscall. + let encoded = raw.wrapping_neg() as u16; + + // TODO: Use Range::contains, once that's `const`. + assert!(encoded >= 0xf001); + + // SAFETY: Linux syscalls return negated error values in the range + // `-4095..0`, which we just asserted. + unsafe { Self(encoded) } + } +} + +/// Check for an error from the result of a syscall which encodes a +/// `c::c_int` on success. +#[inline] +pub(in crate::backend) fn try_decode_c_int( + raw: RetReg, +) -> io::Result { + if raw.is_in_range(-4095..0) { + // SAFETY: `raw` must be in `-4095..0`, and we just checked that raw is + // in that range. + return Err(unsafe { Errno(raw.decode_error_code()) }); + } + + Ok(raw.decode_c_int()) +} + +/// Check for an error from the result of a syscall which encodes a +/// `c::c_uint` on success. +#[inline] +pub(in crate::backend) fn try_decode_c_uint( + raw: RetReg, +) -> io::Result { + if raw.is_in_range(-4095..0) { + // SAFETY: `raw` must be in `-4095..0`, and we just checked that raw is + // in that range. + return Err(unsafe { Errno(raw.decode_error_code()) }); + } + + Ok(raw.decode_c_uint()) +} + +/// Check for an error from the result of a syscall which encodes a `usize` on +/// success. +#[inline] +pub(in crate::backend) fn try_decode_usize(raw: RetReg) -> io::Result { + if raw.is_in_range(-4095..0) { + // SAFETY: `raw` must be in `-4095..0`, and we just checked that raw is + // in that range. + return Err(unsafe { Errno(raw.decode_error_code()) }); + } + + Ok(raw.decode_usize()) +} + +/// Check for an error from the result of a syscall which encodes a +/// `*mut c_void` on success. +#[inline] +pub(in crate::backend) fn try_decode_void_star( + raw: RetReg, +) -> io::Result<*mut c::c_void> { + if raw.is_in_range(-4095..0) { + // SAFETY: `raw` must be in `-4095..0`, and we just checked that raw is + // in that range. + return Err(unsafe { Errno(raw.decode_error_code()) }); + } + + Ok(raw.decode_void_star()) +} + +/// Check for an error from the result of a syscall which encodes a +/// `u64` on success. +#[cfg(target_pointer_width = "64")] +#[inline] +pub(in crate::backend) fn try_decode_u64(raw: RetReg) -> io::Result { + if raw.is_in_range(-4095..0) { + // SAFETY: `raw` must be in `-4095..0`, and we just checked that raw is + // in that range. + return Err(unsafe { Errno(raw.decode_error_code()) }); + } + + Ok(raw.decode_u64()) +} + +/// Check for an error from the result of a syscall which encodes a file +/// descriptor on success. +/// +/// # Safety +/// +/// This must only be used with syscalls which return file descriptors on +/// success. +#[inline] +pub(in crate::backend) unsafe fn try_decode_raw_fd( + raw: RetReg, +) -> io::Result { + // Instead of using `check_result` here, we just check for negative, since + // this function is only used for system calls which return file + // descriptors, and this produces smaller code. + if raw.is_negative() { + debug_assert!(raw.is_in_range(-4095..0)); + + // Tell the optimizer that we know the value is in the error range. + // This helps it avoid unnecessary integer conversions. + #[cfg(core_intrinsics)] + { + core::intrinsics::assume(raw.is_in_range(-4095..0)); + } + + return Err(Errno(raw.decode_error_code())); + } + + Ok(raw.decode_raw_fd()) +} + +/// Check for an error from the result of a syscall which encodes no value on +/// success. On success, return the unconsumed `raw` value. +/// +/// # Safety +/// +/// This must only be used with syscalls which return no value on success. +#[inline] +pub(in crate::backend) unsafe fn try_decode_void( + raw: RetReg, +) -> io::Result<()> { + // Instead of using `check_result` here, we just check for zero, since this + // function is only used for system calls which have no other return value, + // and this produces smaller code. + if raw.is_nonzero() { + debug_assert!(raw.is_in_range(-4095..0)); + + // Tell the optimizer that we know the value is in the error range. + // This helps it avoid unnecessary integer conversions. + #[cfg(core_intrinsics)] + { + core::intrinsics::assume(raw.is_in_range(-4095..0)); + } + + return Err(Errno(raw.decode_error_code())); + } + + raw.decode_void(); + + Ok(()) +} + +/// Check for an error from the result of a syscall which does not return on +/// success. On success, return the unconsumed `raw` value. +/// +/// # Safety +/// +/// This must only be used with syscalls which do not return on success. +#[cfg(any(feature = "event", feature = "runtime", feature = "system"))] +#[inline] +pub(in crate::backend) unsafe fn try_decode_error(raw: RetReg) -> io::Errno { + debug_assert!(raw.is_in_range(-4095..0)); + + // Tell the optimizer that we know the value is in the error range. + // This helps it avoid unnecessary integer conversions. + #[cfg(core_intrinsics)] + { + core::intrinsics::assume(raw.is_in_range(-4095..0)); + } + + Errno(raw.decode_error_code()) +} + +/// Return the contained `usize` value. +#[cfg(not(debug_assertions))] +#[inline] +pub(in crate::backend) fn decode_usize_infallible(raw: RetReg) -> usize { + raw.decode_usize() +} + +/// Return the contained `c_int` value. +#[cfg(not(debug_assertions))] +#[inline] +pub(in crate::backend) fn decode_c_int_infallible(raw: RetReg) -> c::c_int { + raw.decode_c_int() +} + +/// Return the contained `c_uint` value. +#[cfg(not(debug_assertions))] +#[inline] +pub(in crate::backend) fn decode_c_uint_infallible(raw: RetReg) -> c::c_uint { + raw.decode_c_uint() +} + +impl Errno { + /// `EACCES` + #[doc(alias = "ACCES")] + pub const ACCESS: Self = Self::from_errno(errno::EACCES); + /// `EADDRINUSE` + pub const ADDRINUSE: Self = Self::from_errno(errno::EADDRINUSE); + /// `EADDRNOTAVAIL` + pub const ADDRNOTAVAIL: Self = Self::from_errno(errno::EADDRNOTAVAIL); + /// `EADV` + pub const ADV: Self = Self::from_errno(errno::EADV); + /// `EAFNOSUPPORT` + pub const AFNOSUPPORT: Self = Self::from_errno(errno::EAFNOSUPPORT); + /// `EAGAIN` + pub const AGAIN: Self = Self::from_errno(errno::EAGAIN); + /// `EALREADY` + pub const ALREADY: Self = Self::from_errno(errno::EALREADY); + /// `EBADE` + pub const BADE: Self = Self::from_errno(errno::EBADE); + /// `EBADF` + pub const BADF: Self = Self::from_errno(errno::EBADF); + /// `EBADFD` + pub const BADFD: Self = Self::from_errno(errno::EBADFD); + /// `EBADMSG` + pub const BADMSG: Self = Self::from_errno(errno::EBADMSG); + /// `EBADR` + pub const BADR: Self = Self::from_errno(errno::EBADR); + /// `EBADRQC` + pub const BADRQC: Self = Self::from_errno(errno::EBADRQC); + /// `EBADSLT` + pub const BADSLT: Self = Self::from_errno(errno::EBADSLT); + /// `EBFONT` + pub const BFONT: Self = Self::from_errno(errno::EBFONT); + /// `EBUSY` + pub const BUSY: Self = Self::from_errno(errno::EBUSY); + /// `ECANCELED` + pub const CANCELED: Self = Self::from_errno(errno::ECANCELED); + /// `ECHILD` + pub const CHILD: Self = Self::from_errno(errno::ECHILD); + /// `ECHRNG` + pub const CHRNG: Self = Self::from_errno(errno::ECHRNG); + /// `ECOMM` + pub const COMM: Self = Self::from_errno(errno::ECOMM); + /// `ECONNABORTED` + pub const CONNABORTED: Self = Self::from_errno(errno::ECONNABORTED); + /// `ECONNREFUSED` + pub const CONNREFUSED: Self = Self::from_errno(errno::ECONNREFUSED); + /// `ECONNRESET` + pub const CONNRESET: Self = Self::from_errno(errno::ECONNRESET); + /// `EDEADLK` + pub const DEADLK: Self = Self::from_errno(errno::EDEADLK); + /// `EDEADLOCK` + pub const DEADLOCK: Self = Self::from_errno(errno::EDEADLOCK); + /// `EDESTADDRREQ` + pub const DESTADDRREQ: Self = Self::from_errno(errno::EDESTADDRREQ); + /// `EDOM` + pub const DOM: Self = Self::from_errno(errno::EDOM); + /// `EDOTDOT` + pub const DOTDOT: Self = Self::from_errno(errno::EDOTDOT); + /// `EDQUOT` + pub const DQUOT: Self = Self::from_errno(errno::EDQUOT); + /// `EEXIST` + pub const EXIST: Self = Self::from_errno(errno::EEXIST); + /// `EFAULT` + pub const FAULT: Self = Self::from_errno(errno::EFAULT); + /// `EFBIG` + pub const FBIG: Self = Self::from_errno(errno::EFBIG); + /// `EHOSTDOWN` + pub const HOSTDOWN: Self = Self::from_errno(errno::EHOSTDOWN); + /// `EHOSTUNREACH` + pub const HOSTUNREACH: Self = Self::from_errno(errno::EHOSTUNREACH); + /// `EHWPOISON` + pub const HWPOISON: Self = Self::from_errno(errno::EHWPOISON); + /// `EIDRM` + pub const IDRM: Self = Self::from_errno(errno::EIDRM); + /// `EILSEQ` + pub const ILSEQ: Self = Self::from_errno(errno::EILSEQ); + /// `EINPROGRESS` + pub const INPROGRESS: Self = Self::from_errno(errno::EINPROGRESS); + /// `EINTR` + /// + /// For a convenient way to retry system calls that exit with `INTR`, use + /// [`retry_on_intr`]. + /// + /// [`retry_on_intr`]: io::retry_on_intr + pub const INTR: Self = Self::from_errno(errno::EINTR); + /// `EINVAL` + pub const INVAL: Self = Self::from_errno(errno::EINVAL); + /// `EIO` + pub const IO: Self = Self::from_errno(errno::EIO); + /// `EISCONN` + pub const ISCONN: Self = Self::from_errno(errno::EISCONN); + /// `EISDIR` + pub const ISDIR: Self = Self::from_errno(errno::EISDIR); + /// `EISNAM` + pub const ISNAM: Self = Self::from_errno(errno::EISNAM); + /// `EKEYEXPIRED` + pub const KEYEXPIRED: Self = Self::from_errno(errno::EKEYEXPIRED); + /// `EKEYREJECTED` + pub const KEYREJECTED: Self = Self::from_errno(errno::EKEYREJECTED); + /// `EKEYREVOKED` + pub const KEYREVOKED: Self = Self::from_errno(errno::EKEYREVOKED); + /// `EL2HLT` + pub const L2HLT: Self = Self::from_errno(errno::EL2HLT); + /// `EL2NSYNC` + pub const L2NSYNC: Self = Self::from_errno(errno::EL2NSYNC); + /// `EL3HLT` + pub const L3HLT: Self = Self::from_errno(errno::EL3HLT); + /// `EL3RST` + pub const L3RST: Self = Self::from_errno(errno::EL3RST); + /// `ELIBACC` + pub const LIBACC: Self = Self::from_errno(errno::ELIBACC); + /// `ELIBBAD` + pub const LIBBAD: Self = Self::from_errno(errno::ELIBBAD); + /// `ELIBEXEC` + pub const LIBEXEC: Self = Self::from_errno(errno::ELIBEXEC); + /// `ELIBMAX` + pub const LIBMAX: Self = Self::from_errno(errno::ELIBMAX); + /// `ELIBSCN` + pub const LIBSCN: Self = Self::from_errno(errno::ELIBSCN); + /// `ELNRNG` + pub const LNRNG: Self = Self::from_errno(errno::ELNRNG); + /// `ELOOP` + pub const LOOP: Self = Self::from_errno(errno::ELOOP); + /// `EMEDIUMTYPE` + pub const MEDIUMTYPE: Self = Self::from_errno(errno::EMEDIUMTYPE); + /// `EMFILE` + pub const MFILE: Self = Self::from_errno(errno::EMFILE); + /// `EMLINK` + pub const MLINK: Self = Self::from_errno(errno::EMLINK); + /// `EMSGSIZE` + pub const MSGSIZE: Self = Self::from_errno(errno::EMSGSIZE); + /// `EMULTIHOP` + pub const MULTIHOP: Self = Self::from_errno(errno::EMULTIHOP); + /// `ENAMETOOLONG` + pub const NAMETOOLONG: Self = Self::from_errno(errno::ENAMETOOLONG); + /// `ENAVAIL` + pub const NAVAIL: Self = Self::from_errno(errno::ENAVAIL); + /// `ENETDOWN` + pub const NETDOWN: Self = Self::from_errno(errno::ENETDOWN); + /// `ENETRESET` + pub const NETRESET: Self = Self::from_errno(errno::ENETRESET); + /// `ENETUNREACH` + pub const NETUNREACH: Self = Self::from_errno(errno::ENETUNREACH); + /// `ENFILE` + pub const NFILE: Self = Self::from_errno(errno::ENFILE); + /// `ENOANO` + pub const NOANO: Self = Self::from_errno(errno::ENOANO); + /// `ENOBUFS` + pub const NOBUFS: Self = Self::from_errno(errno::ENOBUFS); + /// `ENOCSI` + pub const NOCSI: Self = Self::from_errno(errno::ENOCSI); + /// `ENODATA` + #[doc(alias = "NOATTR")] + pub const NODATA: Self = Self::from_errno(errno::ENODATA); + /// `ENODEV` + pub const NODEV: Self = Self::from_errno(errno::ENODEV); + /// `ENOENT` + pub const NOENT: Self = Self::from_errno(errno::ENOENT); + /// `ENOEXEC` + pub const NOEXEC: Self = Self::from_errno(errno::ENOEXEC); + /// `ENOKEY` + pub const NOKEY: Self = Self::from_errno(errno::ENOKEY); + /// `ENOLCK` + pub const NOLCK: Self = Self::from_errno(errno::ENOLCK); + /// `ENOLINK` + pub const NOLINK: Self = Self::from_errno(errno::ENOLINK); + /// `ENOMEDIUM` + pub const NOMEDIUM: Self = Self::from_errno(errno::ENOMEDIUM); + /// `ENOMEM` + pub const NOMEM: Self = Self::from_errno(errno::ENOMEM); + /// `ENOMSG` + pub const NOMSG: Self = Self::from_errno(errno::ENOMSG); + /// `ENONET` + pub const NONET: Self = Self::from_errno(errno::ENONET); + /// `ENOPKG` + pub const NOPKG: Self = Self::from_errno(errno::ENOPKG); + /// `ENOPROTOOPT` + pub const NOPROTOOPT: Self = Self::from_errno(errno::ENOPROTOOPT); + /// `ENOSPC` + pub const NOSPC: Self = Self::from_errno(errno::ENOSPC); + /// `ENOSR` + pub const NOSR: Self = Self::from_errno(errno::ENOSR); + /// `ENOSTR` + pub const NOSTR: Self = Self::from_errno(errno::ENOSTR); + /// `ENOSYS` + pub const NOSYS: Self = Self::from_errno(errno::ENOSYS); + /// `ENOTBLK` + pub const NOTBLK: Self = Self::from_errno(errno::ENOTBLK); + /// `ENOTCONN` + pub const NOTCONN: Self = Self::from_errno(errno::ENOTCONN); + /// `ENOTDIR` + pub const NOTDIR: Self = Self::from_errno(errno::ENOTDIR); + /// `ENOTEMPTY` + pub const NOTEMPTY: Self = Self::from_errno(errno::ENOTEMPTY); + /// `ENOTNAM` + pub const NOTNAM: Self = Self::from_errno(errno::ENOTNAM); + /// `ENOTRECOVERABLE` + pub const NOTRECOVERABLE: Self = Self::from_errno(errno::ENOTRECOVERABLE); + /// `ENOTSOCK` + pub const NOTSOCK: Self = Self::from_errno(errno::ENOTSOCK); + /// `ENOTSUP` + // On Linux, `ENOTSUP` has the same value as `EOPNOTSUPP`. + pub const NOTSUP: Self = Self::from_errno(errno::EOPNOTSUPP); + /// `ENOTTY` + pub const NOTTY: Self = Self::from_errno(errno::ENOTTY); + /// `ENOTUNIQ` + pub const NOTUNIQ: Self = Self::from_errno(errno::ENOTUNIQ); + /// `ENXIO` + pub const NXIO: Self = Self::from_errno(errno::ENXIO); + /// `EOPNOTSUPP` + pub const OPNOTSUPP: Self = Self::from_errno(errno::EOPNOTSUPP); + /// `EOVERFLOW` + pub const OVERFLOW: Self = Self::from_errno(errno::EOVERFLOW); + /// `EOWNERDEAD` + pub const OWNERDEAD: Self = Self::from_errno(errno::EOWNERDEAD); + /// `EPERM` + pub const PERM: Self = Self::from_errno(errno::EPERM); + /// `EPFNOSUPPORT` + pub const PFNOSUPPORT: Self = Self::from_errno(errno::EPFNOSUPPORT); + /// `EPIPE` + pub const PIPE: Self = Self::from_errno(errno::EPIPE); + /// `EPROTO` + pub const PROTO: Self = Self::from_errno(errno::EPROTO); + /// `EPROTONOSUPPORT` + pub const PROTONOSUPPORT: Self = Self::from_errno(errno::EPROTONOSUPPORT); + /// `EPROTOTYPE` + pub const PROTOTYPE: Self = Self::from_errno(errno::EPROTOTYPE); + /// `ERANGE` + pub const RANGE: Self = Self::from_errno(errno::ERANGE); + /// `EREMCHG` + pub const REMCHG: Self = Self::from_errno(errno::EREMCHG); + /// `EREMOTE` + pub const REMOTE: Self = Self::from_errno(errno::EREMOTE); + /// `EREMOTEIO` + pub const REMOTEIO: Self = Self::from_errno(errno::EREMOTEIO); + /// `ERESTART` + pub const RESTART: Self = Self::from_errno(errno::ERESTART); + /// `ERFKILL` + pub const RFKILL: Self = Self::from_errno(errno::ERFKILL); + /// `EROFS` + pub const ROFS: Self = Self::from_errno(errno::EROFS); + /// `ESHUTDOWN` + pub const SHUTDOWN: Self = Self::from_errno(errno::ESHUTDOWN); + /// `ESOCKTNOSUPPORT` + pub const SOCKTNOSUPPORT: Self = Self::from_errno(errno::ESOCKTNOSUPPORT); + /// `ESPIPE` + pub const SPIPE: Self = Self::from_errno(errno::ESPIPE); + /// `ESRCH` + pub const SRCH: Self = Self::from_errno(errno::ESRCH); + /// `ESRMNT` + pub const SRMNT: Self = Self::from_errno(errno::ESRMNT); + /// `ESTALE` + pub const STALE: Self = Self::from_errno(errno::ESTALE); + /// `ESTRPIPE` + pub const STRPIPE: Self = Self::from_errno(errno::ESTRPIPE); + /// `ETIME` + pub const TIME: Self = Self::from_errno(errno::ETIME); + /// `ETIMEDOUT` + pub const TIMEDOUT: Self = Self::from_errno(errno::ETIMEDOUT); + /// `E2BIG` + #[doc(alias = "2BIG")] + pub const TOOBIG: Self = Self::from_errno(errno::E2BIG); + /// `ETOOMANYREFS` + pub const TOOMANYREFS: Self = Self::from_errno(errno::ETOOMANYREFS); + /// `ETXTBSY` + pub const TXTBSY: Self = Self::from_errno(errno::ETXTBSY); + /// `EUCLEAN` + pub const UCLEAN: Self = Self::from_errno(errno::EUCLEAN); + /// `EUNATCH` + pub const UNATCH: Self = Self::from_errno(errno::EUNATCH); + /// `EUSERS` + pub const USERS: Self = Self::from_errno(errno::EUSERS); + /// `EWOULDBLOCK` + pub const WOULDBLOCK: Self = Self::from_errno(errno::EWOULDBLOCK); + /// `EXDEV` + pub const XDEV: Self = Self::from_errno(errno::EXDEV); + /// `EXFULL` + pub const XFULL: Self = Self::from_errno(errno::EXFULL); +} diff --git a/vendor/rustix/src/backend/linux_raw/io/mod.rs b/vendor/rustix/src/backend/linux_raw/io/mod.rs new file mode 100644 index 00000000..9477b9b9 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/io/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod errno; +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/linux_raw/io/syscalls.rs b/vendor/rustix/src/backend/linux_raw/io/syscalls.rs new file mode 100644 index 00000000..f40dfe5e --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/io/syscalls.rs @@ -0,0 +1,364 @@ +//! linux_raw syscalls supporting `rustix::io`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code)] +#![allow(clippy::undocumented_unsafe_blocks)] + +#[cfg(target_pointer_width = "64")] +use crate::backend::conv::loff_t_from_u64; +#[cfg(all( + target_pointer_width = "32", + any( + target_arch = "arm", + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "powerpc" + ), +))] +use crate::backend::conv::zero; +use crate::backend::conv::{ + c_uint, pass_usize, raw_fd, ret, ret_c_int, ret_c_uint, ret_discarded_fd, ret_owned_fd, + ret_usize, slice, +}; +#[cfg(target_pointer_width = "32")] +use crate::backend::conv::{hi, lo}; +use crate::backend::{c, MAX_IOV}; +use crate::fd::{AsFd as _, BorrowedFd, OwnedFd, RawFd}; +use crate::io::{self, DupFlags, FdFlags, IoSlice, IoSliceMut, ReadWriteFlags}; +use crate::ioctl::{IoctlOutput, Opcode}; +use core::cmp; +use linux_raw_sys::general::{F_DUPFD_CLOEXEC, F_GETFD, F_SETFD}; + +#[inline] +pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: (*mut u8, usize)) -> io::Result { + ret_usize(syscall!(__NR_read, fd, buf.0, pass_usize(buf.1))) +} + +#[inline] +pub(crate) unsafe fn pread( + fd: BorrowedFd<'_>, + buf: (*mut u8, usize), + pos: u64, +) -> io::Result { + // + #[cfg(all( + target_pointer_width = "32", + any( + target_arch = "arm", + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "powerpc" + ), + ))] + { + ret_usize(syscall!( + __NR_pread64, + fd, + buf.0, + pass_usize(buf.1), + zero(), + hi(pos), + lo(pos) + )) + } + #[cfg(all( + target_pointer_width = "32", + not(any( + target_arch = "arm", + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "powerpc" + )), + ))] + { + ret_usize(syscall!( + __NR_pread64, + fd, + buf.0, + pass_usize(buf.1), + hi(pos), + lo(pos) + )) + } + #[cfg(target_pointer_width = "64")] + ret_usize(syscall!( + __NR_pread64, + fd, + buf.0, + pass_usize(buf.1), + loff_t_from_u64(pos) + )) +} + +#[inline] +pub(crate) fn readv(fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]); + + unsafe { ret_usize(syscall!(__NR_readv, fd, bufs_addr, bufs_len)) } +} + +#[inline] +pub(crate) fn preadv( + fd: BorrowedFd<'_>, + bufs: &mut [IoSliceMut<'_>], + pos: u64, +) -> io::Result { + let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]); + + // Unlike the plain "p" functions, the "pv" functions pass their offset in + // an endian-independent way, and always in two registers. + unsafe { + ret_usize(syscall!( + __NR_preadv, + fd, + bufs_addr, + bufs_len, + pass_usize(pos as usize), + pass_usize((pos >> 32) as usize) + )) + } +} + +#[inline] +pub(crate) fn preadv2( + fd: BorrowedFd<'_>, + bufs: &mut [IoSliceMut<'_>], + pos: u64, + flags: ReadWriteFlags, +) -> io::Result { + let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]); + + // Unlike the plain "p" functions, the "pv" functions pass their offset in + // an endian-independent way, and always in two registers. + unsafe { + ret_usize(syscall!( + __NR_preadv2, + fd, + bufs_addr, + bufs_len, + pass_usize(pos as usize), + pass_usize((pos >> 32) as usize), + flags + )) + } +} + +#[inline] +pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result { + let (buf_addr, buf_len) = slice(buf); + + unsafe { ret_usize(syscall_readonly!(__NR_write, fd, buf_addr, buf_len)) } +} + +#[inline] +pub(crate) fn pwrite(fd: BorrowedFd<'_>, buf: &[u8], pos: u64) -> io::Result { + let (buf_addr, buf_len) = slice(buf); + + // + #[cfg(all( + target_pointer_width = "32", + any( + target_arch = "arm", + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "powerpc" + ), + ))] + unsafe { + ret_usize(syscall_readonly!( + __NR_pwrite64, + fd, + buf_addr, + buf_len, + zero(), + hi(pos), + lo(pos) + )) + } + #[cfg(all( + target_pointer_width = "32", + not(any( + target_arch = "arm", + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "powerpc" + )), + ))] + unsafe { + ret_usize(syscall_readonly!( + __NR_pwrite64, + fd, + buf_addr, + buf_len, + hi(pos), + lo(pos) + )) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret_usize(syscall_readonly!( + __NR_pwrite64, + fd, + buf_addr, + buf_len, + loff_t_from_u64(pos) + )) + } +} + +#[inline] +pub(crate) fn writev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>]) -> io::Result { + let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]); + + unsafe { ret_usize(syscall_readonly!(__NR_writev, fd, bufs_addr, bufs_len)) } +} + +#[inline] +pub(crate) fn pwritev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>], pos: u64) -> io::Result { + let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]); + + // Unlike the plain "p" functions, the "pv" functions pass their offset in + // an endian-independent way, and always in two registers. + unsafe { + ret_usize(syscall_readonly!( + __NR_pwritev, + fd, + bufs_addr, + bufs_len, + pass_usize(pos as usize), + pass_usize((pos >> 32) as usize) + )) + } +} + +#[inline] +pub(crate) fn pwritev2( + fd: BorrowedFd<'_>, + bufs: &[IoSlice<'_>], + pos: u64, + flags: ReadWriteFlags, +) -> io::Result { + let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]); + + // Unlike the plain "p" functions, the "pv" functions pass their offset in + // an endian-independent way, and always in two registers. + unsafe { + ret_usize(syscall_readonly!( + __NR_pwritev2, + fd, + bufs_addr, + bufs_len, + pass_usize(pos as usize), + pass_usize((pos >> 32) as usize), + flags + )) + } +} + +#[inline] +pub(crate) unsafe fn close(fd: RawFd) { + // See the documentation for [`io::close`] for why errors are ignored. + syscall_readonly!(__NR_close, raw_fd(fd)).decode_void(); +} + +#[cfg(feature = "try_close")] +#[inline] +pub(crate) unsafe fn try_close(fd: RawFd) -> io::Result<()> { + ret(syscall_readonly!(__NR_close, raw_fd(fd))) +} + +#[inline] +pub(crate) unsafe fn ioctl( + fd: BorrowedFd<'_>, + request: Opcode, + arg: *mut c::c_void, +) -> io::Result { + ret_c_int(syscall!(__NR_ioctl, fd, c_uint(request), arg)) +} + +#[inline] +pub(crate) unsafe fn ioctl_readonly( + fd: BorrowedFd<'_>, + request: Opcode, + arg: *mut c::c_void, +) -> io::Result { + ret_c_int(syscall_readonly!(__NR_ioctl, fd, c_uint(request), arg)) +} + +#[inline] +pub(crate) fn dup(fd: BorrowedFd<'_>) -> io::Result { + unsafe { ret_owned_fd(syscall_readonly!(__NR_dup, fd)) } +} + +#[allow(clippy::needless_pass_by_ref_mut)] +#[inline] +pub(crate) fn dup2(fd: BorrowedFd<'_>, new: &mut OwnedFd) -> io::Result<()> { + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + { + // 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. + dup3(fd, new, DupFlags::empty()) + } + + #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] + unsafe { + ret_discarded_fd(syscall_readonly!(__NR_dup2, fd, new.as_fd())) + } +} + +#[allow(clippy::needless_pass_by_ref_mut)] +#[inline] +pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, flags: DupFlags) -> io::Result<()> { + unsafe { ret_discarded_fd(syscall_readonly!(__NR_dup3, fd, new.as_fd(), flags)) } +} + +#[inline] +pub(crate) fn fcntl_getfd(fd: BorrowedFd<'_>) -> io::Result { + #[cfg(target_pointer_width = "32")] + unsafe { + ret_c_uint(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETFD))) + .map(FdFlags::from_bits_retain) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret_c_uint(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETFD))) + .map(FdFlags::from_bits_retain) + } +} + +#[inline] +pub(crate) fn fcntl_setfd(fd: BorrowedFd<'_>, flags: FdFlags) -> io::Result<()> { + #[cfg(target_pointer_width = "32")] + unsafe { + ret(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_SETFD), flags)) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret(syscall_readonly!(__NR_fcntl, fd, c_uint(F_SETFD), flags)) + } +} + +#[inline] +pub(crate) fn fcntl_dupfd_cloexec(fd: BorrowedFd<'_>, min: RawFd) -> io::Result { + #[cfg(target_pointer_width = "32")] + unsafe { + ret_owned_fd(syscall_readonly!( + __NR_fcntl64, + fd, + c_uint(F_DUPFD_CLOEXEC), + raw_fd(min) + )) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret_owned_fd(syscall_readonly!( + __NR_fcntl, + fd, + c_uint(F_DUPFD_CLOEXEC), + raw_fd(min) + )) + } +} diff --git a/vendor/rustix/src/backend/linux_raw/io/types.rs b/vendor/rustix/src/backend/linux_raw/io/types.rs new file mode 100644 index 00000000..533f9732 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/io/types.rs @@ -0,0 +1,57 @@ +use crate::ffi; +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: ffi::c_uint { + /// `FD_CLOEXEC` + const CLOEXEC = linux_raw_sys::general::FD_CLOEXEC; + + /// + const _ = !0; + } +} + +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: ffi::c_uint { + /// `RWF_DSYNC` (since Linux 4.7) + const DSYNC = linux_raw_sys::general::RWF_DSYNC; + /// `RWF_HIPRI` (since Linux 4.6) + const HIPRI = linux_raw_sys::general::RWF_HIPRI; + /// `RWF_SYNC` (since Linux 4.7) + const SYNC = linux_raw_sys::general::RWF_SYNC; + /// `RWF_NOWAIT` (since Linux 4.14) + const NOWAIT = linux_raw_sys::general::RWF_NOWAIT; + /// `RWF_APPEND` (since Linux 4.16) + const APPEND = linux_raw_sys::general::RWF_APPEND; + + /// + const _ = !0; + } +} + +bitflags! { + /// `O_*` constants for use with [`dup2`]. + /// + /// [`dup2`]: crate::io::dup2 + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct DupFlags: ffi::c_uint { + /// `O_CLOEXEC` + const CLOEXEC = linux_raw_sys::general::O_CLOEXEC; + + /// + const _ = !0; + } +} diff --git a/vendor/rustix/src/backend/linux_raw/io_uring/mod.rs b/vendor/rustix/src/backend/linux_raw/io_uring/mod.rs new file mode 100644 index 00000000..ef944f04 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/io_uring/mod.rs @@ -0,0 +1 @@ +pub(crate) mod syscalls; diff --git a/vendor/rustix/src/backend/linux_raw/io_uring/syscalls.rs b/vendor/rustix/src/backend/linux_raw/io_uring/syscalls.rs new file mode 100644 index 00000000..3e61b7aa --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/io_uring/syscalls.rs @@ -0,0 +1,79 @@ +//! linux_raw syscalls supporting `rustix::io_uring`. +//! +//! # Safety +//! +//! See the `rustix::backend::syscalls` module documentation for details. +#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + +use crate::backend::conv::{by_mut, c_uint, pass_usize, ret_c_uint, ret_owned_fd}; +use crate::fd::{BorrowedFd, OwnedFd}; +use crate::io; +use crate::io_uring::{io_uring_params, IoringEnterFlags, IoringRegisterFlags, IoringRegisterOp}; +use core::ffi::c_void; + +#[inline] +pub(crate) fn io_uring_setup(entries: u32, params: &mut io_uring_params) -> io::Result { + unsafe { + ret_owned_fd(syscall!( + __NR_io_uring_setup, + c_uint(entries), + by_mut(params) + )) + } +} + +#[inline] +pub(crate) unsafe fn io_uring_register( + fd: BorrowedFd<'_>, + opcode: IoringRegisterOp, + arg: *const c_void, + nr_args: u32, +) -> io::Result { + ret_c_uint(syscall_readonly!( + __NR_io_uring_register, + fd, + c_uint(opcode as u32), + arg, + c_uint(nr_args) + )) +} + +#[inline] +pub(crate) unsafe fn io_uring_register_with( + fd: BorrowedFd<'_>, + opcode: IoringRegisterOp, + flags: IoringRegisterFlags, + arg: *const c_void, + nr_args: u32, +) -> io::Result { + ret_c_uint(syscall_readonly!( + __NR_io_uring_register, + fd, + c_uint((opcode as u32) | bitflags_bits!(flags)), + arg, + c_uint(nr_args) + )) +} + +#[inline] +pub(crate) unsafe fn io_uring_enter( + fd: BorrowedFd<'_>, + to_submit: u32, + min_complete: u32, + flags: IoringEnterFlags, + arg: *const c_void, + size: usize, +) -> io::Result { + // This is not `_readonly` because `io_uring_enter` waits for I/O to + // complete, and I/O could involve writing to memory buffers, which + // could be a side effect depended on by the caller. + ret_c_uint(syscall!( + __NR_io_uring_enter, + fd, + c_uint(to_submit), + c_uint(min_complete), + flags, + arg, + pass_usize(size) + )) +} diff --git a/vendor/rustix/src/backend/linux_raw/mm/mod.rs b/vendor/rustix/src/backend/linux_raw/mm/mod.rs new file mode 100644 index 00000000..1e0181a9 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/mm/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/linux_raw/mm/syscalls.rs b/vendor/rustix/src/backend/linux_raw/mm/syscalls.rs new file mode 100644 index 00000000..84881906 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/mm/syscalls.rs @@ -0,0 +1,239 @@ +//! linux_raw syscalls supporting `rustix::io`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code)] +#![allow(clippy::undocumented_unsafe_blocks)] + +use super::types::{ + Advice, MapFlags, MlockAllFlags, MlockFlags, MprotectFlags, MremapFlags, MsyncFlags, ProtFlags, + UserfaultfdFlags, +}; +use crate::backend::c; +#[cfg(target_pointer_width = "64")] +use crate::backend::conv::loff_t_from_u64; +use crate::backend::conv::{c_uint, no_fd, pass_usize, ret, ret_owned_fd, ret_void_star}; +use crate::fd::{BorrowedFd, OwnedFd}; +use crate::ffi::c_void; +use crate::io; +use linux_raw_sys::general::{MAP_ANONYMOUS, MREMAP_FIXED}; + +#[inline] +pub(crate) fn madvise(addr: *mut c_void, len: usize, advice: Advice) -> io::Result<()> { + unsafe { + ret(syscall!( + __NR_madvise, + addr, + pass_usize(len), + c_uint(advice as c::c_uint) + )) + } +} + +#[inline] +pub(crate) unsafe fn msync(addr: *mut c_void, len: usize, flags: MsyncFlags) -> io::Result<()> { + ret(syscall!(__NR_msync, addr, pass_usize(len), flags)) +} + +/// # Safety +/// +/// `mmap` is primarily unsafe due to the `addr` parameter, as anything working +/// with memory pointed to by raw pointers is unsafe. +#[inline] +pub(crate) unsafe fn mmap( + addr: *mut c_void, + length: usize, + prot: ProtFlags, + flags: MapFlags, + fd: BorrowedFd<'_>, + offset: u64, +) -> io::Result<*mut c_void> { + #[cfg(target_pointer_width = "32")] + { + ret_void_star(syscall!( + __NR_mmap2, + addr, + pass_usize(length), + prot, + flags, + fd, + (offset / 4096) + .try_into() + .map(pass_usize) + .map_err(|_| io::Errno::INVAL)? + )) + } + #[cfg(target_pointer_width = "64")] + { + ret_void_star(syscall!( + __NR_mmap, + addr, + pass_usize(length), + prot, + flags, + fd, + loff_t_from_u64(offset) + )) + } +} + +/// # Safety +/// +/// `mmap` is primarily unsafe due to the `addr` parameter, as anything working +/// with memory pointed to by raw pointers is unsafe. +#[inline] +pub(crate) unsafe fn mmap_anonymous( + addr: *mut c_void, + length: usize, + prot: ProtFlags, + flags: MapFlags, +) -> io::Result<*mut c_void> { + #[cfg(target_pointer_width = "32")] + { + ret_void_star(syscall!( + __NR_mmap2, + addr, + pass_usize(length), + prot, + c_uint(flags.bits() | MAP_ANONYMOUS), + no_fd(), + pass_usize(0) + )) + } + #[cfg(target_pointer_width = "64")] + { + ret_void_star(syscall!( + __NR_mmap, + addr, + pass_usize(length), + prot, + c_uint(flags.bits() | MAP_ANONYMOUS), + no_fd(), + loff_t_from_u64(0) + )) + } +} + +#[inline] +pub(crate) unsafe fn mprotect( + ptr: *mut c_void, + len: usize, + flags: MprotectFlags, +) -> io::Result<()> { + ret(syscall!(__NR_mprotect, ptr, pass_usize(len), flags)) +} + +/// # Safety +/// +/// `munmap` is primarily unsafe due to the `addr` parameter, as anything +/// working with memory pointed to by raw pointers is unsafe. +#[inline] +pub(crate) unsafe fn munmap(addr: *mut c_void, length: usize) -> io::Result<()> { + ret(syscall!(__NR_munmap, addr, pass_usize(length))) +} + +/// # Safety +/// +/// `mremap` is primarily unsafe due to the `old_address` parameter, as +/// anything working with memory pointed to by raw pointers is unsafe. +#[inline] +pub(crate) unsafe fn mremap( + old_address: *mut c_void, + old_size: usize, + new_size: usize, + flags: MremapFlags, +) -> io::Result<*mut c_void> { + ret_void_star(syscall!( + __NR_mremap, + old_address, + pass_usize(old_size), + pass_usize(new_size), + flags + )) +} + +/// # 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. +#[inline] +pub(crate) unsafe fn mremap_fixed( + old_address: *mut c_void, + old_size: usize, + new_size: usize, + flags: MremapFlags, + new_address: *mut c_void, +) -> io::Result<*mut c_void> { + ret_void_star(syscall!( + __NR_mremap, + old_address, + pass_usize(old_size), + pass_usize(new_size), + c_uint(flags.bits() | MREMAP_FIXED), + new_address + )) +} + +/// # Safety +/// +/// `mlock` operates on raw pointers and may round out to the nearest page +/// boundaries. +#[inline] +pub(crate) unsafe fn mlock(addr: *mut c_void, length: usize) -> io::Result<()> { + ret(syscall!(__NR_mlock, addr, pass_usize(length))) +} + +/// # Safety +/// +/// `mlock_with` operates on raw pointers and may round out to the nearest page +/// boundaries. +#[inline] +pub(crate) unsafe fn mlock_with( + addr: *mut c_void, + length: usize, + flags: MlockFlags, +) -> io::Result<()> { + ret(syscall!(__NR_mlock2, addr, pass_usize(length), 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_void, length: usize) -> io::Result<()> { + ret(syscall!(__NR_munlock, addr, pass_usize(length))) +} + +#[inline] +pub(crate) unsafe fn userfaultfd(flags: UserfaultfdFlags) -> io::Result { + ret_owned_fd(syscall_readonly!(__NR_userfaultfd, 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] +pub(crate) fn mlockall(flags: MlockAllFlags) -> io::Result<()> { + // When `mlockall` is used with `MCL_ONFAULT | MCL_FUTURE`, the ordering + // of `mlockall` with respect to arbitrary loads may be significant, + // because if a load happens and evokes a fault before the `mlockall`, + // the memory doesn't get locked, but if the load and therefore + // the fault happens after, then the memory does get locked. + // + // So to be conservative in this regard, we use `syscall` instead of + // `syscall_readonly` + unsafe { ret(syscall!(__NR_mlockall, flags)) } +} + +/// Unlocks all pages mapped into the address space of the calling process. +#[inline] +pub(crate) fn munlockall() -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_munlockall)) } +} diff --git a/vendor/rustix/src/backend/linux_raw/mm/types.rs b/vendor/rustix/src/backend/linux_raw/mm/types.rs new file mode 100644 index 00000000..c6806fc4 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/mm/types.rs @@ -0,0 +1,297 @@ +use crate::ffi; +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 = linux_raw_sys::general::PROT_READ; + /// `PROT_WRITE` + const WRITE = linux_raw_sys::general::PROT_WRITE; + /// `PROT_EXEC` + const EXEC = linux_raw_sys::general::PROT_EXEC; + + /// + 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 = linux_raw_sys::general::PROT_READ; + /// `PROT_WRITE` + const WRITE = linux_raw_sys::general::PROT_WRITE; + /// `PROT_EXEC` + const EXEC = linux_raw_sys::general::PROT_EXEC; + /// `PROT_GROWSUP` + const GROWSUP = linux_raw_sys::general::PROT_GROWSUP; + /// `PROT_GROWSDOWN` + const GROWSDOWN = linux_raw_sys::general::PROT_GROWSDOWN; + /// `PROT_SEM` + const SEM = linux_raw_sys::general::PROT_SEM; + /// `PROT_BTI` + #[cfg(target_arch = "aarch64")] + const BTI = linux_raw_sys::general::PROT_BTI; + /// `PROT_MTE` + #[cfg(target_arch = "aarch64")] + const MTE = linux_raw_sys::general::PROT_MTE; + /// `PROT_SAO` + #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] + const SAO = linux_raw_sys::general::PROT_SAO; + /// `PROT_ADI` + #[cfg(any(target_arch = "sparc", target_arch = "sparc64"))] + const ADI = linux_raw_sys::general::PROT_ADI; + + /// + 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 = linux_raw_sys::general::MAP_SHARED; + /// `MAP_SHARED_VALIDATE` (since Linux 4.15) + const SHARED_VALIDATE = linux_raw_sys::general::MAP_SHARED_VALIDATE; + /// `MAP_PRIVATE` + const PRIVATE = linux_raw_sys::general::MAP_PRIVATE; + /// `MAP_DENYWRITE` + const DENYWRITE = linux_raw_sys::general::MAP_DENYWRITE; + /// `MAP_FIXED` + const FIXED = linux_raw_sys::general::MAP_FIXED; + /// `MAP_FIXED_NOREPLACE` (since Linux 4.17) + const FIXED_NOREPLACE = linux_raw_sys::general::MAP_FIXED_NOREPLACE; + /// `MAP_GROWSDOWN` + const GROWSDOWN = linux_raw_sys::general::MAP_GROWSDOWN; + /// `MAP_HUGETLB` + const HUGETLB = linux_raw_sys::general::MAP_HUGETLB; + /// `MAP_HUGE_2MB` (since Linux 3.8) + const HUGE_2MB = linux_raw_sys::general::MAP_HUGE_2MB; + /// `MAP_HUGE_1GB` (since Linux 3.8) + const HUGE_1GB = linux_raw_sys::general::MAP_HUGE_1GB; + /// `MAP_LOCKED` + const LOCKED = linux_raw_sys::general::MAP_LOCKED; + /// `MAP_NORESERVE` + const NORESERVE = linux_raw_sys::general::MAP_NORESERVE; + /// `MAP_POPULATE` + const POPULATE = linux_raw_sys::general::MAP_POPULATE; + /// `MAP_STACK` + const STACK = linux_raw_sys::general::MAP_STACK; + /// `MAP_SYNC` (since Linux 4.15) + #[cfg(not(any(target_arch = "mips", target_arch = "mips32r6", target_arch = "mips64", target_arch = "mips64r6")))] + const SYNC = linux_raw_sys::general::MAP_SYNC; + /// `MAP_UNINITIALIZED` + #[cfg(not(any(target_arch = "mips", target_arch = "mips32r6", target_arch = "mips64", target_arch = "mips64r6")))] + const UNINITIALIZED = linux_raw_sys::general::MAP_UNINITIALIZED; + /// `MAP_DROPPABLE` + const DROPPABLE = linux_raw_sys::general::MAP_DROPPABLE; + + /// + const _ = !0; + } +} + +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 = linux_raw_sys::general::MREMAP_MAYMOVE; + /// `MREMAP_DONTUNMAP` (since Linux 5.7) + const DONTUNMAP = linux_raw_sys::general::MREMAP_DONTUNMAP; + + /// + 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 = linux_raw_sys::general::MS_SYNC; + /// `MS_ASYNC`—Specifies that an update be scheduled, but the call + /// returns immediately. + const ASYNC = linux_raw_sys::general::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 = linux_raw_sys::general::MS_INVALIDATE; + + /// + const _ = !0; + } +} + +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 = linux_raw_sys::general::MLOCK_ONFAULT; + + /// + const _ = !0; + } +} + +/// `POSIX_MADV_*` constants for use with [`madvise`]. +/// +/// [`madvise`]: crate::mm::madvise +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +#[non_exhaustive] +pub enum Advice { + /// `POSIX_MADV_NORMAL` + Normal = linux_raw_sys::general::MADV_NORMAL, + + /// `POSIX_MADV_SEQUENTIAL` + Sequential = linux_raw_sys::general::MADV_SEQUENTIAL, + + /// `POSIX_MADV_RANDOM` + Random = linux_raw_sys::general::MADV_RANDOM, + + /// `POSIX_MADV_WILLNEED` + WillNeed = linux_raw_sys::general::MADV_WILLNEED, + + /// `MADV_DONTNEED` + LinuxDontNeed = linux_raw_sys::general::MADV_DONTNEED, + + /// `MADV_FREE` (since Linux 4.5) + LinuxFree = linux_raw_sys::general::MADV_FREE, + /// `MADV_REMOVE` + LinuxRemove = linux_raw_sys::general::MADV_REMOVE, + /// `MADV_DONTFORK` + LinuxDontFork = linux_raw_sys::general::MADV_DONTFORK, + /// `MADV_DOFORK` + LinuxDoFork = linux_raw_sys::general::MADV_DOFORK, + /// `MADV_HWPOISON` + LinuxHwPoison = linux_raw_sys::general::MADV_HWPOISON, + /// `MADV_SOFT_OFFLINE` + #[cfg(not(any( + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "mips64", + target_arch = "mips64r6" + )))] + LinuxSoftOffline = linux_raw_sys::general::MADV_SOFT_OFFLINE, + /// `MADV_MERGEABLE` + LinuxMergeable = linux_raw_sys::general::MADV_MERGEABLE, + /// `MADV_UNMERGEABLE` + LinuxUnmergeable = linux_raw_sys::general::MADV_UNMERGEABLE, + /// `MADV_HUGEPAGE` + LinuxHugepage = linux_raw_sys::general::MADV_HUGEPAGE, + /// `MADV_NOHUGEPAGE` + LinuxNoHugepage = linux_raw_sys::general::MADV_NOHUGEPAGE, + /// `MADV_DONTDUMP` (since Linux 3.4) + LinuxDontDump = linux_raw_sys::general::MADV_DONTDUMP, + /// `MADV_DODUMP` (since Linux 3.4) + LinuxDoDump = linux_raw_sys::general::MADV_DODUMP, + /// `MADV_WIPEONFORK` (since Linux 4.14) + LinuxWipeOnFork = linux_raw_sys::general::MADV_WIPEONFORK, + /// `MADV_KEEPONFORK` (since Linux 4.14) + LinuxKeepOnFork = linux_raw_sys::general::MADV_KEEPONFORK, + /// `MADV_COLD` (since Linux 5.4) + LinuxCold = linux_raw_sys::general::MADV_COLD, + /// `MADV_PAGEOUT` (since Linux 5.4) + LinuxPageOut = linux_raw_sys::general::MADV_PAGEOUT, + /// `MADV_POPULATE_READ` (since Linux 5.14) + LinuxPopulateRead = linux_raw_sys::general::MADV_POPULATE_READ, + /// `MADV_POPULATE_WRITE` (since Linux 5.14) + LinuxPopulateWrite = linux_raw_sys::general::MADV_POPULATE_WRITE, + /// `MADV_DONTNEED_LOCKED` (since Linux 5.18) + LinuxDontneedLocked = linux_raw_sys::general::MADV_DONTNEED_LOCKED, +} + +#[allow(non_upper_case_globals)] +impl Advice { + /// `POSIX_MADV_DONTNEED` + /// + /// On Linux, this is mapped to `POSIX_MADV_NORMAL` because Linux's + /// `MADV_DONTNEED` differs from `POSIX_MADV_DONTNEED`. See `LinuxDontNeed` + /// for the Linux behavior. + pub const DontNeed: Self = Self::Normal; +} + +bitflags! { + /// `O_*` flags for use with [`userfaultfd`]. + /// + /// [`userfaultfd`]: crate::mm::userfaultfd + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct UserfaultfdFlags: ffi::c_uint { + /// `O_CLOEXEC` + const CLOEXEC = linux_raw_sys::general::O_CLOEXEC; + /// `O_NONBLOCK` + const NONBLOCK = linux_raw_sys::general::O_NONBLOCK; + + /// + const _ = !0; + } +} + +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. + const ONFAULT = linux_raw_sys::general::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 = linux_raw_sys::general::MCL_FUTURE; + /// Lock all pages which are currently mapped into the address space of + /// the process. + const CURRENT = linux_raw_sys::general::MCL_CURRENT; + + /// + const _ = !0; + } +} diff --git a/vendor/rustix/src/backend/linux_raw/mod.rs b/vendor/rustix/src/backend/linux_raw/mod.rs new file mode 100644 index 00000000..581dcc97 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/mod.rs @@ -0,0 +1,112 @@ +//! The linux_raw backend. +//! +//! This makes Linux syscalls directly, without going through libc. +//! +//! # Safety +//! +//! These files performs raw system calls, and sometimes passes them +//! uninitialized memory buffers. The signatures in this module are currently +//! manually maintained and must correspond with the signatures of the actual +//! Linux syscalls. +//! +//! Some of this could be auto-generated from the Linux header file +//! , but we often need more information than it provides, +//! such as which pointers are array slices, out parameters, or in-out +//! parameters, which integers are owned or borrowed file descriptors, etc. + +#[macro_use] +mod arch; +mod conv; +mod reg; +#[cfg(any(feature = "time", feature = "thread", target_arch = "x86"))] +mod vdso; +#[cfg(any(feature = "time", feature = "thread", target_arch = "x86"))] +mod vdso_wrappers; + +#[cfg(feature = "event")] +pub(crate) mod event; +#[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) mod fs; +pub(crate) mod io; +#[cfg(feature = "io_uring")] +pub(crate) mod io_uring; +#[cfg(feature = "mm")] +pub(crate) mod mm; +#[cfg(feature = "mount")] +pub(crate) mod mount; +#[cfg(feature = "net")] +pub(crate) mod net; +#[cfg(any( + feature = "param", + feature = "runtime", + feature = "thread", + feature = "time", + target_arch = "x86", +))] +pub(crate) mod param; +#[cfg(feature = "pipe")] +pub(crate) mod pipe; +#[cfg(feature = "process")] +pub(crate) mod process; +#[cfg(feature = "pty")] +pub(crate) mod pty; +#[cfg(feature = "rand")] +pub(crate) mod rand; +#[cfg(feature = "runtime")] +pub(crate) mod runtime; +#[cfg(feature = "shm")] +pub(crate) mod shm; +#[cfg(feature = "system")] +pub(crate) mod system; +#[cfg(feature = "termios")] +pub(crate) mod termios; +#[cfg(feature = "thread")] +pub(crate) mod thread; +#[cfg(feature = "time")] +pub(crate) mod time; + +// Re-export the maybe-polyfill `core::os::fd`. +pub(crate) use crate::maybe_polyfill::os::fd; + +// The linux_raw backend doesn't use actual libc, so we define selected +// libc-like definitions in a module called `c`. +pub(crate) mod c; + +// Private modules used by multiple public modules. +#[cfg(any(feature = "process", feature = "runtime"))] +pub(crate) mod pid; +#[cfg(any(feature = "process", feature = "thread"))] +pub(crate) mod prctl; +#[cfg(any( + feature = "fs", + feature = "process", + feature = "thread", + all( + not(feature = "use-libc-auxv"), + not(feature = "use-explicitly-provided-auxv"), + any( + feature = "param", + feature = "runtime", + feature = "time", + target_arch = "x86", + ) + ) +))] +pub(crate) mod ugid; + +/// The maximum number of buffers that can be passed into a vectored I/O system +/// call on the current platform. +const MAX_IOV: usize = linux_raw_sys::general::UIO_MAXIOV as usize; diff --git a/vendor/rustix/src/backend/linux_raw/mount/mod.rs b/vendor/rustix/src/backend/linux_raw/mount/mod.rs new file mode 100644 index 00000000..1e0181a9 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/mount/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/linux_raw/mount/syscalls.rs b/vendor/rustix/src/backend/linux_raw/mount/syscalls.rs new file mode 100644 index 00000000..6fc69240 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/mount/syscalls.rs @@ -0,0 +1,237 @@ +//! linux_raw syscalls supporting `rustix::mount`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code)] +#![allow(clippy::undocumented_unsafe_blocks)] + +use crate::backend::conv::{ret, ret_owned_fd, slice, zero}; +use crate::fd::{BorrowedFd, OwnedFd}; +use crate::ffi::CStr; +use crate::io; + +#[inline] +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(syscall_readonly!( + __NR_mount, + source, + target, + file_system_type, + flags, + data + )) + } +} + +#[inline] +pub(crate) fn unmount(target: &CStr, flags: super::types::UnmountFlags) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_umount2, target, flags)) } +} + +#[inline] +pub(crate) fn fsopen(fs_name: &CStr, flags: super::types::FsOpenFlags) -> io::Result { + unsafe { ret_owned_fd(syscall_readonly!(__NR_fsopen, fs_name, flags)) } +} + +#[inline] +pub(crate) fn fsmount( + fs_fd: BorrowedFd<'_>, + flags: super::types::FsMountFlags, + attr_flags: super::types::MountAttrFlags, +) -> io::Result { + unsafe { ret_owned_fd(syscall_readonly!(__NR_fsmount, fs_fd, flags, attr_flags)) } +} + +#[inline] +pub(crate) fn move_mount( + from_dfd: BorrowedFd<'_>, + from_pathname: &CStr, + to_dfd: BorrowedFd<'_>, + to_pathname: &CStr, + flags: super::types::MoveMountFlags, +) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_move_mount, + from_dfd, + from_pathname, + to_dfd, + to_pathname, + flags + )) + } +} + +#[inline] +pub(crate) fn open_tree( + dfd: BorrowedFd<'_>, + filename: &CStr, + flags: super::types::OpenTreeFlags, +) -> io::Result { + unsafe { ret_owned_fd(syscall_readonly!(__NR_open_tree, dfd, filename, flags)) } +} + +#[inline] +pub(crate) fn fspick( + dfd: BorrowedFd<'_>, + path: &CStr, + flags: super::types::FsPickFlags, +) -> io::Result { + unsafe { ret_owned_fd(syscall_readonly!(__NR_fspick, dfd, path, flags)) } +} + +#[inline] +pub(crate) fn fsconfig_set_flag(fs_fd: BorrowedFd<'_>, key: &CStr) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_fsconfig, + fs_fd, + super::types::FsConfigCmd::SetFlag, + key, + zero(), + zero() + )) + } +} + +#[inline] +pub(crate) fn fsconfig_set_string( + fs_fd: BorrowedFd<'_>, + key: &CStr, + value: &CStr, +) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_fsconfig, + fs_fd, + super::types::FsConfigCmd::SetString, + key, + value, + zero() + )) + } +} + +#[inline] +pub(crate) fn fsconfig_set_binary( + fs_fd: BorrowedFd<'_>, + key: &CStr, + value: &[u8], +) -> io::Result<()> { + let (value_addr, value_len) = slice(value); + unsafe { + ret(syscall_readonly!( + __NR_fsconfig, + fs_fd, + super::types::FsConfigCmd::SetBinary, + key, + value_addr, + value_len + )) + } +} + +#[inline] +pub(crate) fn fsconfig_set_fd( + fs_fd: BorrowedFd<'_>, + key: &CStr, + fd: BorrowedFd<'_>, +) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_fsconfig, + fs_fd, + super::types::FsConfigCmd::SetFd, + key, + zero(), + fd + )) + } +} + +#[inline] +pub(crate) fn fsconfig_set_path( + fs_fd: BorrowedFd<'_>, + key: &CStr, + path: &CStr, + fd: BorrowedFd<'_>, +) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_fsconfig, + fs_fd, + super::types::FsConfigCmd::SetPath, + key, + path, + fd + )) + } +} + +#[inline] +pub(crate) fn fsconfig_set_path_empty( + fs_fd: BorrowedFd<'_>, + key: &CStr, + fd: BorrowedFd<'_>, +) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_fsconfig, + fs_fd, + super::types::FsConfigCmd::SetPathEmpty, + key, + cstr!(""), + fd + )) + } +} + +#[inline] +pub(crate) fn fsconfig_create(fs_fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_fsconfig, + fs_fd, + super::types::FsConfigCmd::Create, + zero(), + zero(), + zero() + )) + } +} + +#[inline] +pub(crate) fn fsconfig_reconfigure(fs_fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_fsconfig, + fs_fd, + super::types::FsConfigCmd::Reconfigure, + zero(), + zero(), + zero() + )) + } +} + +#[inline] +pub(crate) fn fsconfig_create_excl(fs_fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_fsconfig, + fs_fd, + super::types::FsConfigCmd::CreateExclusive, + zero(), + zero(), + zero() + )) + } +} diff --git a/vendor/rustix/src/backend/linux_raw/mount/types.rs b/vendor/rustix/src/backend/linux_raw/mount/types.rs new file mode 100644 index 00000000..548b74ed --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/mount/types.rs @@ -0,0 +1,335 @@ +use crate::ffi; +use bitflags::bitflags; + +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_uint { + /// `MS_BIND` + const BIND = linux_raw_sys::general::MS_BIND; + + /// `MS_DIRSYNC` + const DIRSYNC = linux_raw_sys::general::MS_DIRSYNC; + + /// `MS_LAZYTIME` + const LAZYTIME = linux_raw_sys::general::MS_LAZYTIME; + + /// `MS_MANDLOCK` + #[doc(alias = "MANDLOCK")] + const PERMIT_MANDATORY_FILE_LOCKING = linux_raw_sys::general::MS_MANDLOCK; + + /// `MS_NOATIME` + const NOATIME = linux_raw_sys::general::MS_NOATIME; + + /// `MS_NODEV` + const NODEV = linux_raw_sys::general::MS_NODEV; + + /// `MS_NODIRATIME` + const NODIRATIME = linux_raw_sys::general::MS_NODIRATIME; + + /// `MS_NOEXEC` + const NOEXEC = linux_raw_sys::general::MS_NOEXEC; + + /// `MS_NOSUID` + const NOSUID = linux_raw_sys::general::MS_NOSUID; + + /// `MS_RDONLY` + const RDONLY = linux_raw_sys::general::MS_RDONLY; + + /// `MS_REC` + const REC = linux_raw_sys::general::MS_REC; + + /// `MS_RELATIME` + const RELATIME = linux_raw_sys::general::MS_RELATIME; + + /// `MS_SILENT` + const SILENT = linux_raw_sys::general::MS_SILENT; + + /// `MS_STRICTATIME` + const STRICTATIME = linux_raw_sys::general::MS_STRICTATIME; + + /// `MS_SYNCHRONOUS` + const SYNCHRONOUS = linux_raw_sys::general::MS_SYNCHRONOUS; + + /// `MS_NOSYMFOLLOW` + const NOSYMFOLLOW = linux_raw_sys::general::MS_NOSYMFOLLOW; + + /// + const _ = !0; + } +} + +bitflags! { + /// `MNT_*` constants for use with [`unmount`]. + /// + /// [`unmount`]: crate::mount::unmount + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct UnmountFlags: ffi::c_uint { + /// `MNT_FORCE` + const FORCE = linux_raw_sys::general::MNT_FORCE; + /// `MNT_DETACH` + const DETACH = linux_raw_sys::general::MNT_DETACH; + /// `MNT_EXPIRE` + const EXPIRE = linux_raw_sys::general::MNT_EXPIRE; + /// `UMOUNT_NOFOLLOW` + const NOFOLLOW = linux_raw_sys::general::UMOUNT_NOFOLLOW; + + /// + const _ = !0; + } +} + +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 = linux_raw_sys::general::FSOPEN_CLOEXEC; + + /// + const _ = !0; + } +} + +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 = linux_raw_sys::general::FSMOUNT_CLOEXEC; + + /// + const _ = !0; + } +} + +/// `FSCONFIG_*` constants for use with the `fsconfig` syscall. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +pub(crate) enum FsConfigCmd { + /// `FSCONFIG_SET_FLAG` + SetFlag = linux_raw_sys::general::fsconfig_command::FSCONFIG_SET_FLAG as u32, + + /// `FSCONFIG_SET_STRING` + SetString = linux_raw_sys::general::fsconfig_command::FSCONFIG_SET_STRING as u32, + + /// `FSCONFIG_SET_BINARY` + SetBinary = linux_raw_sys::general::fsconfig_command::FSCONFIG_SET_BINARY as u32, + + /// `FSCONFIG_SET_PATH` + SetPath = linux_raw_sys::general::fsconfig_command::FSCONFIG_SET_PATH as u32, + + /// `FSCONFIG_SET_PATH_EMPTY` + SetPathEmpty = linux_raw_sys::general::fsconfig_command::FSCONFIG_SET_PATH_EMPTY as u32, + + /// `FSCONFIG_SET_FD` + SetFd = linux_raw_sys::general::fsconfig_command::FSCONFIG_SET_FD as u32, + + /// `FSCONFIG_CMD_CREATE` + Create = linux_raw_sys::general::fsconfig_command::FSCONFIG_CMD_CREATE as u32, + + /// `FSCONFIG_CMD_RECONFIGURE` + Reconfigure = linux_raw_sys::general::fsconfig_command::FSCONFIG_CMD_RECONFIGURE as u32, + + /// `FSCONFIG_CMD_CREATE_EXCL` (since Linux 6.6) + CreateExclusive = linux_raw_sys::general::fsconfig_command::FSCONFIG_CMD_CREATE_EXCL as u32, +} + +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 = linux_raw_sys::general::MOUNT_ATTR_RDONLY; + + /// `MOUNT_ATTR_NOSUID` + const MOUNT_ATTR_NOSUID = linux_raw_sys::general::MOUNT_ATTR_NOSUID; + + /// `MOUNT_ATTR_NODEV` + const MOUNT_ATTR_NODEV = linux_raw_sys::general::MOUNT_ATTR_NODEV; + + /// `MOUNT_ATTR_NOEXEC` + const MOUNT_ATTR_NOEXEC = linux_raw_sys::general::MOUNT_ATTR_NOEXEC; + + /// `MOUNT_ATTR__ATIME` + const MOUNT_ATTR__ATIME = linux_raw_sys::general::MOUNT_ATTR__ATIME; + + /// `MOUNT_ATTR_RELATIME` + const MOUNT_ATTR_RELATIME = linux_raw_sys::general::MOUNT_ATTR_RELATIME; + + /// `MOUNT_ATTR_NOATIME` + const MOUNT_ATTR_NOATIME = linux_raw_sys::general::MOUNT_ATTR_NOATIME; + + /// `MOUNT_ATTR_STRICTATIME` + const MOUNT_ATTR_STRICTATIME = linux_raw_sys::general::MOUNT_ATTR_STRICTATIME; + + /// `MOUNT_ATTR_NODIRATIME` + const MOUNT_ATTR_NODIRATIME = linux_raw_sys::general::MOUNT_ATTR_NODIRATIME; + + /// `MOUNT_ATTR_NOUSER` + const MOUNT_ATTR_IDMAP = linux_raw_sys::general::MOUNT_ATTR_IDMAP; + + /// `MOUNT_ATTR__ATIME_FLAGS` + const MOUNT_ATTR_NOSYMFOLLOW = linux_raw_sys::general::MOUNT_ATTR_NOSYMFOLLOW; + + /// `MOUNT_ATTR__ATIME_FLAGS` + const MOUNT_ATTR_SIZE_VER0 = linux_raw_sys::general::MOUNT_ATTR_SIZE_VER0; + + /// + const _ = !0; + } +} + +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 = linux_raw_sys::general::MOVE_MOUNT_F_SYMLINKS; + + /// `MOVE_MOUNT_F_AUTOMOUNTS` + const MOVE_MOUNT_F_AUTOMOUNTS = linux_raw_sys::general::MOVE_MOUNT_F_AUTOMOUNTS; + + /// `MOVE_MOUNT_F_EMPTY_PATH` + const MOVE_MOUNT_F_EMPTY_PATH = linux_raw_sys::general::MOVE_MOUNT_F_EMPTY_PATH; + + /// `MOVE_MOUNT_T_SYMLINKS` + const MOVE_MOUNT_T_SYMLINKS = linux_raw_sys::general::MOVE_MOUNT_T_SYMLINKS; + + /// `MOVE_MOUNT_T_AUTOMOUNTS` + const MOVE_MOUNT_T_AUTOMOUNTS = linux_raw_sys::general::MOVE_MOUNT_T_AUTOMOUNTS; + + /// `MOVE_MOUNT_T_EMPTY_PATH` + const MOVE_MOUNT_T_EMPTY_PATH = linux_raw_sys::general::MOVE_MOUNT_T_EMPTY_PATH; + + /// `MOVE_MOUNT__MASK` + const MOVE_MOUNT_SET_GROUP = linux_raw_sys::general::MOVE_MOUNT_SET_GROUP; + + /// `MOVE_MOUNT_BENEATH` (since Linux 6.5) + const MOVE_MOUNT_BENEATH = linux_raw_sys::general::MOVE_MOUNT_BENEATH; + + /// `MOVE_MOUNT__MASK` + const MOVE_MOUNT__MASK = linux_raw_sys::general::MOVE_MOUNT__MASK; + + /// + const _ = !0; + } +} + +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 = linux_raw_sys::general::OPEN_TREE_CLONE; + + /// `OPENTREE_CLOEXEC` + const OPEN_TREE_CLOEXEC = linux_raw_sys::general::OPEN_TREE_CLOEXEC; + + /// `AT_EMPTY_PATH` + const AT_EMPTY_PATH = linux_raw_sys::general::AT_EMPTY_PATH; + + /// `AT_NO_AUTOMOUNT` + const AT_NO_AUTOMOUNT = linux_raw_sys::general::AT_NO_AUTOMOUNT; + + /// `AT_RECURSIVE` + const AT_RECURSIVE = linux_raw_sys::general::AT_RECURSIVE; + + /// `AT_SYMLINK_NOFOLLOW` + const AT_SYMLINK_NOFOLLOW = linux_raw_sys::general::AT_SYMLINK_NOFOLLOW; + + /// + const _ = !0; + } +} + +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 = linux_raw_sys::general::FSPICK_CLOEXEC; + + /// `FSPICK_SYMLINK_NOFOLLOW` + const FSPICK_SYMLINK_NOFOLLOW = linux_raw_sys::general::FSPICK_SYMLINK_NOFOLLOW; + + /// `FSPICK_NO_AUTOMOUNT` + const FSPICK_NO_AUTOMOUNT = linux_raw_sys::general::FSPICK_NO_AUTOMOUNT; + + /// `FSPICK_EMPTY_PATH` + const FSPICK_EMPTY_PATH = linux_raw_sys::general::FSPICK_EMPTY_PATH; + + /// + const _ = !0; + } +} + +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_uint { + /// `MS_SILENT` + const SILENT = linux_raw_sys::general::MS_SILENT; + /// `MS_SHARED` + const SHARED = linux_raw_sys::general::MS_SHARED; + /// `MS_PRIVATE` + const PRIVATE = linux_raw_sys::general::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 = linux_raw_sys::general::MS_SLAVE; + /// `MS_UNBINDABLE` + const UNBINDABLE = linux_raw_sys::general::MS_UNBINDABLE; + /// `MS_REC` + const REC = linux_raw_sys::general::MS_REC; + + /// + const _ = !0; + } +} + +bitflags! { + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub(crate) struct InternalMountFlags: ffi::c_uint { + const REMOUNT = linux_raw_sys::general::MS_REMOUNT; + const MOVE = linux_raw_sys::general::MS_MOVE; + + /// + const _ = !0; + } +} + +#[repr(transparent)] +pub(crate) struct MountFlagsArg(pub(crate) ffi::c_uint); diff --git a/vendor/rustix/src/backend/linux_raw/net/addr.rs b/vendor/rustix/src/backend/linux_raw/net/addr.rs new file mode 100644 index 00000000..7138b571 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/net/addr.rs @@ -0,0 +1,300 @@ +//! Socket address utilities. +//! +//! # Safety +//! +//! This file uses `CStr::from_bytes_with_nul_unchecked` on a string it knows +//! to be NUL-terminated. +#![allow(unsafe_code)] + +use crate::backend::c; +use crate::ffi::CStr; +use crate::net::addr::SocketAddrLen; +use crate::net::AddressFamily; +use crate::{io, path}; +use core::cmp::Ordering; +use core::hash::{Hash, Hasher}; +use core::{fmt, slice}; +#[cfg(feature = "alloc")] +use {crate::ffi::CString, alloc::borrow::Cow, alloc::vec::Vec}; + +/// `struct sockaddr_un` +#[derive(Clone)] +#[doc(alias = "sockaddr_un")] +pub struct SocketAddrUnix { + pub(crate) unix: c::sockaddr_un, + len: c::socklen_t, +} + +impl SocketAddrUnix { + /// Construct a new Unix-domain address from a filesystem path. + #[inline] + pub fn new(path: P) -> io::Result { + path.into_with_c_str(Self::_new) + } + + #[inline] + fn _new(path: &CStr) -> io::Result { + 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] = bitcast!(*b); + } + let len = offsetof_sun_path() + bytes.len(); + let len = len.try_into().unwrap(); + Ok(Self { unix, len }) + } + + /// Construct a new abstract Unix-domain address from a byte slice. + #[inline] + pub fn new_abstract_name(name: &[u8]) -> io::Result { + let mut unix = Self::init(); + let id = &mut unix.sun_path[1..]; + + // SAFETY: Convert `&mut [c_char]` to `&mut [u8]`. + let id = unsafe { slice::from_raw_parts_mut(id.as_mut_ptr().cast::(), id.len()) }; + + if let Some(id) = id.get_mut(..name.len()) { + id.copy_from_slice(name); + let len = offsetof_sun_path() + 1 + name.len(); + let len = len.try_into().unwrap(); + Ok(Self { unix, len }) + } else { + Err(io::Errno::NAMETOOLONG) + } + } + + /// 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 + #[inline] + pub fn new_unnamed() -> Self { + Self { + unix: Self::init(), + len: offsetof_sun_path() as SocketAddrLen, + } + } + + const fn init() -> c::sockaddr_un { + c::sockaddr_un { + sun_family: c::AF_UNIX as _, + sun_path: [0; 108], + } + } + + /// For a filesystem path address, return the path. + #[inline] + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + pub fn path(&self) -> Option> { + 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> { + 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. + #[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. + #[inline] + pub fn is_unnamed(&self) -> bool { + self.bytes() == Some(&[]) + } + + #[inline] + pub(crate) fn addr_len(&self) -> SocketAddrLen { + bitcast!(self.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 + } + } +} + +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]) + } +} + +impl Eq for SocketAddrUnix {} + +impl PartialOrd for SocketAddrUnix { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +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]) + } +} + +impl Hash for SocketAddrUnix { + #[inline] + fn hash(&self, state: &mut H) { + let self_len = self.len() - offsetof_sun_path(); + self.unix.sun_path[..self_len].hash(state) + } +} + +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); + } + 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); + +// SAFETY: Bindgen adds a union with a raw pointer for alignment but it's never +// used. `sockaddr_storage` is just a bunch of bytes and it doesn't hold +// pointers. +unsafe impl Send for SocketAddrStorage {} + +// SAFETY: Same as with `Send`. +unsafe impl Sync for SocketAddrStorage {} + +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::(), + )) + } + } + + /// 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::(), + ) + } + } +} + +/// Return the offset of the `sun_path` field of `sockaddr_un`. +#[inline] +pub(crate) fn offsetof_sun_path() -> usize { + let z = c::sockaddr_un { + sun_family: 0_u16, + sun_path: [0; 108], + }; + (crate::utils::as_ptr(&z.sun_path) as usize) - (crate::utils::as_ptr(&z) as usize) +} diff --git a/vendor/rustix/src/backend/linux_raw/net/mod.rs b/vendor/rustix/src/backend/linux_raw/net/mod.rs new file mode 100644 index 00000000..169954f2 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/net/mod.rs @@ -0,0 +1,8 @@ +pub(crate) mod addr; +pub(crate) mod msghdr; +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; diff --git a/vendor/rustix/src/backend/linux_raw/net/msghdr.rs b/vendor/rustix/src/backend/linux_raw/net/msghdr.rs new file mode 100644 index 00000000..0343e18c --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/net/msghdr.rs @@ -0,0 +1,125 @@ +//! 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. + +#![allow(unsafe_code)] + +use crate::backend::c; + +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::net::addr::SocketAddrArg; +use crate::net::{RecvAncillaryBuffer, SendAncillaryBuffer, SocketAddrBuf}; + +use core::ptr::null_mut; + +fn msg_iov_len(len: usize) -> c::size_t { + // This cast cannot overflow. + len as c::size_t +} + +fn msg_control_len(len: usize) -> c::size_t { + // Same as above. + len as c::size_t +} + +/// 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( + name: &mut SocketAddrBuf, + iov: &mut [IoSliceMut<'_>], + control: &mut RecvAncillaryBuffer<'_>, + f: impl FnOnce(&mut c::msghdr) -> io::Result, +) -> io::Result { + control.clear(); + + let mut msghdr = c::msghdr { + msg_name: name.storage.as_mut_ptr().cast(), + msg_namelen: bitcast!(name.len), + msg_iov: iov.as_mut_ptr().cast(), + msg_iovlen: msg_iov_len(iov.len()), + msg_control: control.as_control_ptr().cast(), + msg_controllen: msg_control_len(control.control_len()), + msg_flags: 0, + }; + + 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 = bitcast!(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 { + c::msghdr { + msg_name: null_mut(), + msg_namelen: 0, + msg_iov: iov.as_ptr() as _, + msg_iovlen: msg_iov_len(iov.len()), + msg_control: control.as_control_ptr().cast(), + msg_controllen: msg_control_len(control.control_len()), + msg_flags: 0, + } +} + +/// 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( + addr: &impl SocketAddrArg, + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + f: impl FnOnce(&c::msghdr) -> R, +) -> R { + addr.with_sockaddr(|addr_ptr, addr_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`. + let mut msghdr = noaddr_msghdr(iov, control); + msghdr.msg_name = addr_ptr as _; + msghdr.msg_namelen = bitcast!(addr_len); + + f(&msghdr) + }) +} + +/// Create a zero-initialized message header struct value. +pub(crate) fn zero_msghdr() -> c::msghdr { + c::msghdr { + msg_name: null_mut(), + msg_namelen: 0, + msg_iov: null_mut(), + msg_iovlen: 0, + msg_control: null_mut(), + msg_controllen: 0, + msg_flags: 0, + } +} diff --git a/vendor/rustix/src/backend/linux_raw/net/netdevice.rs b/vendor/rustix/src/backend/linux_raw/net/netdevice.rs new file mode 100644 index 00000000..d3c2a964 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/net/netdevice.rs @@ -0,0 +1,70 @@ +//! Wrappers for netdevice ioctls. + +#![allow(unsafe_code)] + +use crate::backend::io::syscalls::ioctl; +use crate::fd::BorrowedFd; +use crate::io; +use core::ptr::addr_of_mut; +use core::{slice, str}; +use linux_raw_sys::ctypes::c_char; +use linux_raw_sys::ioctl::SIOCGIFINDEX; +#[cfg(feature = "alloc")] +use linux_raw_sys::ioctl::SIOCGIFNAME; +use linux_raw_sys::net::{ifreq, ifreq__bindgen_ty_1, ifreq__bindgen_ty_2, IFNAMSIZ}; +#[cfg(feature = "alloc")] +use {alloc::borrow::ToOwned, alloc::string::String}; + +pub(crate) fn name_to_index(fd: BorrowedFd<'_>, if_name: &str) -> io::Result { + let if_name_bytes = if_name.as_bytes(); + if if_name_bytes.len() >= IFNAMSIZ as usize { + return Err(io::Errno::NODEV); + } + if if_name_bytes.contains(&0) { + return Err(io::Errno::NODEV); + } + + // SAFETY: Convert `&[u8]` to `&[c_char]`. + let if_name_bytes = unsafe { + slice::from_raw_parts(if_name_bytes.as_ptr().cast::(), if_name_bytes.len()) + }; + + let mut ifreq = ifreq { + ifr_ifrn: ifreq__bindgen_ty_1 { ifrn_name: [0; 16] }, + ifr_ifru: ifreq__bindgen_ty_2 { ifru_ivalue: 0 }, + }; + unsafe { ifreq.ifr_ifrn.ifrn_name[..if_name_bytes.len()].copy_from_slice(if_name_bytes) }; + + unsafe { ioctl(fd, SIOCGIFINDEX, addr_of_mut!(ifreq).cast()) }?; + let index = unsafe { ifreq.ifr_ifru.ifru_ivalue }; + Ok(index as u32) +} + +#[cfg(feature = "alloc")] +pub(crate) fn index_to_name(fd: BorrowedFd<'_>, index: u32) -> io::Result { + let mut ifreq = ifreq { + ifr_ifrn: ifreq__bindgen_ty_1 { ifrn_name: [0; 16] }, + ifr_ifru: ifreq__bindgen_ty_2 { + ifru_ivalue: index as _, + }, + }; + + unsafe { ioctl(fd, SIOCGIFNAME, addr_of_mut!(ifreq).cast()) }?; + + if let Some(nul_byte) = unsafe { ifreq.ifr_ifrn.ifrn_name } + .iter() + .position(|ch| *ch == 0) + { + let ifrn_name = unsafe { &ifreq.ifr_ifrn.ifrn_name[..nul_byte] }; + + // SAFETY: Convert `&[c_char]` to `&[u8]`. + let ifrn_name = + unsafe { slice::from_raw_parts(ifrn_name.as_ptr().cast::(), ifrn_name.len()) }; + + str::from_utf8(ifrn_name) + .map_err(|_| io::Errno::ILSEQ) + .map(ToOwned::to_owned) + } else { + Err(io::Errno::INVAL) + } +} diff --git a/vendor/rustix/src/backend/linux_raw/net/read_sockaddr.rs b/vendor/rustix/src/backend/linux_raw/net/read_sockaddr.rs new file mode 100644 index 00000000..f18cd483 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/net/read_sockaddr.rs @@ -0,0 +1,155 @@ +//! 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. +#![allow(unsafe_code)] + +use crate::backend::c; +use crate::io::Errno; +use crate::net::addr::SocketAddrLen; +use crate::net::netlink::SocketAddrNetlink; +#[cfg(target_os = "linux")] +use crate::net::xdp::{SocketAddrXdp, SocketAddrXdpFlags}; +use crate::net::{ + AddressFamily, Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrUnix, SocketAddrV4, SocketAddrV6, +}; +use core::mem::size_of; +use core::slice; + +// This must match the header of `sockaddr`. +#[repr(C)] +pub(crate) struct sockaddr_header { + sa_family: u16, +} + +/// Read the `sa_family` field from a socket address returned from the OS. +/// +/// # Safety +/// +/// `storage` must point to a least an initialized `sockaddr_header`. +#[inline] +pub(crate) const unsafe fn read_sa_family(storage: *const c::sockaddr) -> u16 { + // Assert that we know the layout of `sockaddr`. + let _ = c::sockaddr { + __storage: c::sockaddr_storage { + __bindgen_anon_1: linux_raw_sys::net::__kernel_sockaddr_storage__bindgen_ty_1 { + __bindgen_anon_1: + linux_raw_sys::net::__kernel_sockaddr_storage__bindgen_ty_1__bindgen_ty_1 { + ss_family: 0_u16, + __data: [0; 126_usize], + }, + }, + }, + }; + + (*storage.cast::()).sa_family +} + +/// 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`. +#[inline] +pub(crate) unsafe fn initialize_family_to_unspec(storage: *mut c::sockaddr) { + (*storage.cast::()).sa_family = c::AF_UNSPEC as _; +} + +/// Check if a socket address returned from the OS is considered non-empty. +#[inline] +pub(crate) unsafe fn sockaddr_nonempty(_storage: *const c::sockaddr, len: SocketAddrLen) -> bool { + len != 0 +} + +#[inline] +pub(crate) fn read_sockaddr_v4(addr: &SocketAddrAny) -> Result { + if addr.address_family() != AddressFamily::INET { + return Err(Errno::AFNOSUPPORT); + } + assert!(addr.addr_len() as usize >= size_of::()); + let decode = unsafe { &*addr.as_ptr().cast::() }; + Ok(SocketAddrV4::new( + Ipv4Addr::from(u32::from_be(decode.sin_addr.s_addr)), + u16::from_be(decode.sin_port), + )) +} + +#[inline] +pub(crate) fn read_sockaddr_v6(addr: &SocketAddrAny) -> Result { + if addr.address_family() != AddressFamily::INET6 { + return Err(Errno::AFNOSUPPORT); + } + assert!(addr.addr_len() as usize >= size_of::()); + let decode = unsafe { &*addr.as_ptr().cast::() }; + Ok(SocketAddrV6::new( + Ipv6Addr::from(unsafe { decode.sin6_addr.in6_u.u6_addr8 }), + u16::from_be(decode.sin6_port), + u32::from_be(decode.sin6_flowinfo), + decode.sin6_scope_id, + )) +} + +#[inline] +pub(crate) fn read_sockaddr_unix(addr: &SocketAddrAny) -> Result { + 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::() }; + + // On Linux check for Linux's [abstract namespace]. + // + // [abstract namespace]: https://man7.org/linux/man-pages/man7/unix.7.html + if decode.sun_path[0] == 0 { + let bytes = &decode.sun_path[1..len - offsetof_sun_path]; + + // SAFETY: Convert `&[c_char]` to `&[u8]`. + let bytes = unsafe { slice::from_raw_parts(bytes.as_ptr().cast::(), bytes.len()) }; + + return SocketAddrUnix::new_abstract_name(bytes); + } + + // Otherwise we expect a NUL-terminated filesystem path. + let bytes = &decode.sun_path[..len - 1 - offsetof_sun_path]; + + // SAFETY: Convert `&[c_char]` to `&[u8]`. + let bytes = unsafe { slice::from_raw_parts(bytes.as_ptr().cast::(), bytes.len()) }; + + assert_eq!(decode.sun_path[len - 1 - offsetof_sun_path], 0); + SocketAddrUnix::new(bytes) + } +} + +#[inline] +pub(crate) fn read_sockaddr_xdp(addr: &SocketAddrAny) -> Result { + if addr.address_family() != AddressFamily::XDP { + return Err(Errno::AFNOSUPPORT); + } + assert!(addr.addr_len() as usize >= size_of::()); + let decode = unsafe { &*addr.as_ptr().cast::() }; + + // 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), + )) +} + +#[inline] +pub(crate) fn read_sockaddr_netlink(addr: &SocketAddrAny) -> Result { + if addr.address_family() != AddressFamily::NETLINK { + return Err(Errno::AFNOSUPPORT); + } + assert!(addr.addr_len() as usize >= size_of::()); + let decode = unsafe { &*addr.as_ptr().cast::() }; + Ok(SocketAddrNetlink::new(decode.nl_pid, decode.nl_groups)) +} diff --git a/vendor/rustix/src/backend/linux_raw/net/send_recv.rs b/vendor/rustix/src/backend/linux_raw/net/send_recv.rs new file mode 100644 index 00000000..3262d01f --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/net/send_recv.rs @@ -0,0 +1,87 @@ +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` + const CONFIRM = c::MSG_CONFIRM; + /// `MSG_DONTROUTE` + const DONTROUTE = c::MSG_DONTROUTE; + /// `MSG_DONTWAIT` + const DONTWAIT = c::MSG_DONTWAIT; + /// `MSG_EOR` + const EOR = c::MSG_EOR; + /// `MSG_MORE` + const MORE = c::MSG_MORE; + /// `MSG_NOSIGNAL` + const NOSIGNAL = c::MSG_NOSIGNAL; + /// `MSG_OOB` + const OOB = c::MSG_OOB; + + /// + 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` + const CMSG_CLOEXEC = c::MSG_CMSG_CLOEXEC; + /// `MSG_DONTWAIT` + const DONTWAIT = c::MSG_DONTWAIT; + /// `MSG_ERRQUEUE` + const ERRQUEUE = c::MSG_ERRQUEUE; + /// `MSG_OOB` + const OOB = c::MSG_OOB; + /// `MSG_PEEK` + const PEEK = c::MSG_PEEK; + /// `MSG_TRUNC` + const TRUNC = c::MSG_TRUNC; + /// `MSG_WAITALL` + const WAITALL = c::MSG_WAITALL; + + /// + 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 = c::MSG_OOB; + /// `MSG_EOR` + const EOR = c::MSG_EOR; + /// `MSG_TRUNC` + const TRUNC = c::MSG_TRUNC; + /// `MSG_CTRUNC` + const CTRUNC = c::MSG_CTRUNC; + /// `MSG_ERRQUEUE` + const ERRQUEUE = c::MSG_ERRQUEUE; + /// `MSG_CMSG_CLOEXEC` + const CMSG_CLOEXEC = c::MSG_CMSG_CLOEXEC; + + /// + const _ = !0; + } +} diff --git a/vendor/rustix/src/backend/linux_raw/net/sockopt.rs b/vendor/rustix/src/backend/linux_raw/net/sockopt.rs new file mode 100644 index 00000000..3e5ea1fd --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/net/sockopt.rs @@ -0,0 +1,1099 @@ +//! linux_raw syscalls supporting `rustix::net::sockopt`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + +use crate::backend::c; +use crate::backend::conv::{by_mut, c_uint, ret, socklen_t}; +use crate::fd::BorrowedFd; +#[cfg(feature = "alloc")] +use crate::ffi::CStr; +use crate::io; +use crate::net::sockopt::Timeout; +#[cfg(target_os = "linux")] +use crate::net::xdp::{XdpMmapOffsets, XdpOptionsFlags, XdpRingOffset, XdpStatistics, XdpUmemReg}; +use crate::net::{ + AddressFamily, Ipv4Addr, Ipv6Addr, Protocol, RawProtocol, SocketAddrBuf, SocketAddrV4, + SocketAddrV6, SocketType, UCred, +}; +#[cfg(feature = "alloc")] +use alloc::borrow::ToOwned as _; +#[cfg(feature = "alloc")] +use alloc::string::String; +use core::mem::{size_of, MaybeUninit}; +use core::time::Duration; +use linux_raw_sys::general::{__kernel_old_timeval, __kernel_sock_timeval}; +use linux_raw_sys::net::{IPV6_MTU, IPV6_MULTICAST_IF, IP_MTU, IP_MULTICAST_IF}; +#[cfg(target_os = "linux")] +use linux_raw_sys::xdp::{xdp_mmap_offsets, xdp_statistics, xdp_statistics_v1}; +#[cfg(target_arch = "x86")] +use { + crate::backend::conv::{slice_just_addr, x86_sys}, + crate::backend::reg::{ArgReg, SocketArg}, + linux_raw_sys::net::{SYS_GETSOCKOPT, SYS_SETSOCKOPT}, +}; + +#[inline] +fn getsockopt(fd: BorrowedFd<'_>, level: u32, optname: u32) -> io::Result { + let mut optlen: c::socklen_t = size_of::().try_into().unwrap(); + debug_assert!( + optlen as usize >= size_of::(), + "Socket APIs don't ever use `bool` directly" + ); + + let mut value = MaybeUninit::::uninit(); + getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; + + assert_eq!( + optlen as usize, + size_of::(), + "unexpected getsockopt size" + ); + + unsafe { Ok(value.assume_init()) } +} + +#[inline] +fn getsockopt_raw( + fd: BorrowedFd<'_>, + level: u32, + optname: u32, + value: &mut MaybeUninit, + optlen: &mut c::socklen_t, +) -> io::Result<()> { + #[cfg(not(target_arch = "x86"))] + unsafe { + ret(syscall!( + __NR_getsockopt, + fd, + c_uint(level), + c_uint(optname), + value, + by_mut(optlen) + )) + } + #[cfg(target_arch = "x86")] + unsafe { + ret(syscall!( + __NR_socketcall, + x86_sys(SYS_GETSOCKOPT), + slice_just_addr::, _>(&[ + fd.into(), + c_uint(level), + c_uint(optname), + value.into(), + by_mut(optlen), + ]) + )) + } +} + +#[inline] +fn setsockopt(fd: BorrowedFd<'_>, level: u32, optname: u32, value: T) -> io::Result<()> { + let optlen = size_of::().try_into().unwrap(); + debug_assert!( + optlen as usize >= size_of::(), + "Socket APIs don't ever use `bool` directly" + ); + setsockopt_raw(fd, level, optname, &value, optlen) +} + +#[inline] +fn setsockopt_raw( + fd: BorrowedFd<'_>, + level: u32, + optname: u32, + ptr: *const T, + optlen: c::socklen_t, +) -> io::Result<()> { + #[cfg(not(target_arch = "x86"))] + unsafe { + ret(syscall_readonly!( + __NR_setsockopt, + fd, + c_uint(level), + c_uint(optname), + ptr, + socklen_t(optlen) + )) + } + #[cfg(target_arch = "x86")] + unsafe { + ret(syscall_readonly!( + __NR_socketcall, + x86_sys(SYS_SETSOCKOPT), + slice_just_addr::, _>(&[ + fd.into(), + c_uint(level), + c_uint(optname), + ptr.into(), + socklen_t(optlen), + ]) + )) + } +} + +#[inline] +pub(crate) fn socket_type(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, c::SO_TYPE) +} + +#[inline] +pub(crate) fn set_socket_reuseaddr(fd: BorrowedFd<'_>, reuseaddr: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEADDR, from_bool(reuseaddr)) +} + +#[inline] +pub(crate) fn socket_reuseaddr(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEADDR).map(to_bool) +} + +#[inline] +pub(crate) fn set_socket_broadcast(fd: BorrowedFd<'_>, broadcast: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_BROADCAST, from_bool(broadcast)) +} + +#[inline] +pub(crate) fn socket_broadcast(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, c::SO_BROADCAST).map(to_bool) +} + +#[inline] +pub(crate) fn set_socket_linger(fd: BorrowedFd<'_>, linger: Option) -> io::Result<()> { + // Convert `linger` to seconds, rounding up. + let l_linger = if let Some(linger) = linger { + duration_to_secs(linger)? + } else { + 0 + }; + let linger = c::linger { + l_onoff: c::c_int::from(linger.is_some()), + l_linger, + }; + setsockopt(fd, c::SOL_SOCKET, c::SO_LINGER, linger) +} + +#[inline] +pub(crate) fn socket_linger(fd: BorrowedFd<'_>) -> io::Result> { + let linger: c::linger = getsockopt(fd, c::SOL_SOCKET, c::SO_LINGER)?; + Ok((linger.l_onoff != 0).then(|| Duration::from_secs(linger.l_linger as u64))) +} + +#[inline] +pub(crate) fn set_socket_passcred(fd: BorrowedFd<'_>, passcred: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_PASSCRED, from_bool(passcred)) +} + +#[inline] +pub(crate) fn socket_passcred(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, c::SO_PASSCRED).map(to_bool) +} + +#[inline] +pub(crate) fn set_socket_timeout( + fd: BorrowedFd<'_>, + id: Timeout, + timeout: Option, +) -> io::Result<()> { + let time = duration_to_linux_sock_timeval(timeout)?; + let optname = match id { + Timeout::Recv => c::SO_RCVTIMEO_NEW, + Timeout::Send => c::SO_SNDTIMEO_NEW, + }; + match setsockopt(fd, c::SOL_SOCKET, optname, time) { + Err(io::Errno::NOPROTOOPT) if c::SO_RCVTIMEO_NEW != c::SO_RCVTIMEO_OLD => { + set_socket_timeout_old(fd, id, timeout) + } + otherwise => otherwise, + } +} + +/// Same as `set_socket_timeout` but uses `__kernel_old_timeval` instead of +/// `__kernel_sock_timeval` and `_OLD` constants instead of `_NEW`. +fn set_socket_timeout_old( + fd: BorrowedFd<'_>, + id: Timeout, + timeout: Option, +) -> io::Result<()> { + let time = duration_to_linux_old_timeval(timeout)?; + let optname = match id { + Timeout::Recv => c::SO_RCVTIMEO_OLD, + Timeout::Send => c::SO_SNDTIMEO_OLD, + }; + setsockopt(fd, c::SOL_SOCKET, optname, time) +} + +#[inline] +pub(crate) fn socket_timeout(fd: BorrowedFd<'_>, id: Timeout) -> io::Result> { + let optname = match id { + Timeout::Recv => c::SO_RCVTIMEO_NEW, + Timeout::Send => c::SO_SNDTIMEO_NEW, + }; + let time: __kernel_sock_timeval = match getsockopt(fd, c::SOL_SOCKET, optname) { + Err(io::Errno::NOPROTOOPT) if c::SO_RCVTIMEO_NEW != c::SO_RCVTIMEO_OLD => { + return socket_timeout_old(fd, id) + } + otherwise => otherwise?, + }; + Ok(duration_from_linux_sock_timeval(time)) +} + +/// Same as `get_socket_timeout` but uses `__kernel_old_timeval` instead of +/// `__kernel_sock_timeval` and `_OLD` constants instead of `_NEW`. +fn socket_timeout_old(fd: BorrowedFd<'_>, id: Timeout) -> io::Result> { + let optname = match id { + Timeout::Recv => c::SO_RCVTIMEO_OLD, + Timeout::Send => c::SO_SNDTIMEO_OLD, + }; + let time: __kernel_old_timeval = getsockopt(fd, c::SOL_SOCKET, optname)?; + Ok(duration_from_linux_old_timeval(time)) +} + +/// Convert a `__linux_sock_timeval` to a Rust `Option`. +#[inline] +fn duration_from_linux_sock_timeval(time: __kernel_sock_timeval) -> Option { + if time.tv_sec == 0 && time.tv_usec == 0 { + None + } else { + Some(Duration::from_secs(time.tv_sec as u64) + Duration::from_micros(time.tv_usec as u64)) + } +} + +/// Like `duration_from_linux_sock_timeval` but uses Linux's old 32-bit +/// `__kernel_old_timeval`. +fn duration_from_linux_old_timeval(time: __kernel_old_timeval) -> Option { + if time.tv_sec == 0 && time.tv_usec == 0 { + None + } else { + Some(Duration::from_secs(time.tv_sec as u64) + Duration::from_micros(time.tv_usec as u64)) + } +} + +/// Convert a Rust `Option` to a `__kernel_sock_timeval`. +#[inline] +fn duration_to_linux_sock_timeval(timeout: Option) -> io::Result<__kernel_sock_timeval> { + Ok(match timeout { + Some(timeout) => { + if timeout == Duration::ZERO { + return Err(io::Errno::INVAL); + } + // `subsec_micros` rounds down, so we use `subsec_nanos` and + // manually round up. + let mut timeout = __kernel_sock_timeval { + tv_sec: timeout.as_secs().try_into().unwrap_or(i64::MAX), + tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => __kernel_sock_timeval { + tv_sec: 0, + tv_usec: 0, + }, + }) +} + +/// Like `duration_to_linux_sock_timeval` but uses Linux's old 32-bit +/// `__kernel_old_timeval`. +fn duration_to_linux_old_timeval(timeout: Option) -> io::Result<__kernel_old_timeval> { + Ok(match timeout { + Some(timeout) => { + if timeout == Duration::ZERO { + return Err(io::Errno::INVAL); + } + + // `subsec_micros` rounds down, so we use `subsec_nanos` and + // manually round up. + let mut timeout = __kernel_old_timeval { + tv_sec: timeout.as_secs().try_into().unwrap_or(c::c_long::MAX), + tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => __kernel_old_timeval { + tv_sec: 0, + tv_usec: 0, + }, + }) +} + +#[inline] +pub(crate) fn socket_error(fd: BorrowedFd<'_>) -> io::Result> { + let err: c::c_int = getsockopt(fd, c::SOL_SOCKET, c::SO_ERROR)?; + Ok(if err == 0 { + Ok(()) + } else { + Err(io::Errno::from_raw_os_error(err)) + }) +} + +#[inline] +pub(crate) fn set_socket_keepalive(fd: BorrowedFd<'_>, keepalive: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_KEEPALIVE, from_bool(keepalive)) +} + +#[inline] +pub(crate) fn socket_keepalive(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, c::SO_KEEPALIVE).map(to_bool) +} + +#[inline] +pub(crate) fn set_socket_recv_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> { + let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?; + setsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF, size) +} + +#[inline] +pub(crate) fn set_socket_recv_buffer_size_force(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> { + let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?; + setsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUFFORCE, size) +} + +#[inline] +pub(crate) fn socket_recv_buffer_size(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF).map(|size: u32| size as usize) +} + +#[inline] +pub(crate) fn set_socket_send_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> { + let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?; + setsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUF, size) +} + +#[inline] +pub(crate) fn set_socket_send_buffer_size_force(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> { + let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?; + setsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUFFORCE, size) +} + +#[inline] +pub(crate) fn socket_send_buffer_size(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUF).map(|size: u32| size as usize) +} + +#[inline] +pub(crate) fn socket_domain(fd: BorrowedFd<'_>) -> io::Result { + let domain: c::c_int = getsockopt(fd, c::SOL_SOCKET, c::SO_DOMAIN)?; + Ok(AddressFamily( + domain.try_into().map_err(|_| io::Errno::OPNOTSUPP)?, + )) +} + +#[inline] +pub(crate) fn socket_acceptconn(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, c::SO_ACCEPTCONN).map(to_bool) +} + +#[inline] +pub(crate) fn set_socket_oobinline(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE, from_bool(value)) +} + +#[inline] +pub(crate) fn socket_oobinline(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE).map(to_bool) +} + +#[inline] +pub(crate) fn set_socket_reuseport(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT, from_bool(value)) +} + +#[inline] +pub(crate) fn socket_reuseport(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT).map(to_bool) +} + +#[inline] +pub(crate) fn socket_protocol(fd: BorrowedFd<'_>) -> io::Result> { + getsockopt(fd, c::SOL_SOCKET, c::SO_PROTOCOL) + .map(|raw: u32| RawProtocol::new(raw).map(Protocol::from_raw)) +} + +#[inline] +pub(crate) fn socket_cookie(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, c::SO_COOKIE) +} + +#[inline] +pub(crate) fn socket_incoming_cpu(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU) +} + +#[inline] +pub(crate) fn set_socket_incoming_cpu(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU, value) +} + +#[inline] +pub(crate) fn set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_TTL, ttl) +} + +#[inline] +pub(crate) fn ip_ttl(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IP, c::IP_TTL) +} + +#[inline] +pub(crate) fn set_ipv6_v6only(fd: BorrowedFd<'_>, only_v6: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY, from_bool(only_v6)) +} + +#[inline] +pub(crate) fn ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY).map(to_bool) +} + +#[inline] +pub(crate) fn ip_mtu(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IP, IP_MTU) +} + +#[inline] +pub(crate) fn ipv6_mtu(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IPV6, IPV6_MTU) +} + +#[inline] +pub(crate) fn set_ip_multicast_if_with_ifindex( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + address: &Ipv4Addr, + ifindex: u32, +) -> io::Result<()> { + let mreqn = to_ip_mreqn(multiaddr, address, ifindex as i32); + setsockopt(fd, c::IPPROTO_IP, IP_MULTICAST_IF, mreqn) +} + +#[inline] +pub(crate) fn set_ip_multicast_if(fd: BorrowedFd<'_>, value: &Ipv4Addr) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, IP_MULTICAST_IF, to_imr_addr(value)) +} + +#[inline] +pub(crate) fn ip_multicast_if(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IP, IP_MULTICAST_IF).map(from_in_addr) +} + +#[inline] +pub(crate) fn set_ipv6_multicast_if(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, IPV6_MULTICAST_IF, value as c::c_int) +} + +#[inline] +pub(crate) fn ipv6_multicast_if(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IPV6, IPV6_MULTICAST_IF) +} + +#[inline] +pub(crate) fn set_ip_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> { + setsockopt( + fd, + c::IPPROTO_IP, + c::IP_MULTICAST_LOOP, + from_bool(multicast_loop), + ) +} + +#[inline] +pub(crate) fn ip_multicast_loop(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_LOOP).map(to_bool) +} + +#[inline] +pub(crate) fn set_ip_multicast_ttl(fd: BorrowedFd<'_>, multicast_ttl: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_TTL, multicast_ttl) +} + +#[inline] +pub(crate) fn ip_multicast_ttl(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_TTL) +} + +#[inline] +pub(crate) fn set_ipv6_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> { + setsockopt( + fd, + c::IPPROTO_IPV6, + c::IPV6_MULTICAST_LOOP, + from_bool(multicast_loop), + ) +} + +#[inline] +pub(crate) fn ipv6_multicast_loop(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP).map(to_bool) +} + +#[inline] +pub(crate) fn set_ipv6_multicast_hops(fd: BorrowedFd<'_>, multicast_hops: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IPV6_MULTICAST_HOPS, multicast_hops) +} + +#[inline] +pub(crate) fn ipv6_multicast_hops(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IP, c::IPV6_MULTICAST_HOPS) +} + +#[inline] +pub(crate) fn set_ip_add_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + interface: &Ipv4Addr, +) -> io::Result<()> { + let mreq = to_ip_mreq(multiaddr, interface); + setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq) +} + +#[inline] +pub(crate) fn set_ip_add_membership_with_ifindex( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + address: &Ipv4Addr, + ifindex: u32, +) -> io::Result<()> { + let mreqn = to_ip_mreqn(multiaddr, address, ifindex as i32); + setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreqn) +} + +#[inline] +pub(crate) fn set_ip_add_source_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + interface: &Ipv4Addr, + sourceaddr: &Ipv4Addr, +) -> io::Result<()> { + let mreq_source = to_imr_source(multiaddr, interface, sourceaddr); + setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_SOURCE_MEMBERSHIP, mreq_source) +} + +#[inline] +pub(crate) fn set_ip_drop_source_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + interface: &Ipv4Addr, + sourceaddr: &Ipv4Addr, +) -> io::Result<()> { + let mreq_source = to_imr_source(multiaddr, interface, sourceaddr); + setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_SOURCE_MEMBERSHIP, mreq_source) +} + +#[inline] +pub(crate) fn set_ipv6_add_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv6Addr, + interface: u32, +) -> io::Result<()> { + let mreq = to_ipv6mr(multiaddr, interface); + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_ADD_MEMBERSHIP, mreq) +} + +#[inline] +pub(crate) fn set_ip_drop_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + interface: &Ipv4Addr, +) -> io::Result<()> { + let mreq = to_ip_mreq(multiaddr, interface); + setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq) +} + +#[inline] +pub(crate) fn set_ip_drop_membership_with_ifindex( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + address: &Ipv4Addr, + ifindex: u32, +) -> io::Result<()> { + let mreqn = to_ip_mreqn(multiaddr, address, ifindex as i32); + setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreqn) +} + +#[inline] +pub(crate) fn set_ipv6_drop_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv6Addr, + interface: u32, +) -> io::Result<()> { + let mreq = to_ipv6mr(multiaddr, interface); + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_DROP_MEMBERSHIP, mreq) +} + +#[inline] +pub(crate) fn ipv6_unicast_hops(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS).map(|hops: c::c_int| hops as u8) +} + +#[inline] +pub(crate) fn set_ipv6_unicast_hops(fd: BorrowedFd<'_>, hops: Option) -> io::Result<()> { + let hops = match hops { + Some(hops) => hops.into(), + None => -1, + }; + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS, hops) +} + +#[inline] +pub(crate) fn set_ip_tos(fd: BorrowedFd<'_>, value: u8) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_TOS, i32::from(value)) +} + +#[inline] +pub(crate) fn ip_tos(fd: BorrowedFd<'_>) -> io::Result { + let value: i32 = getsockopt(fd, c::IPPROTO_IP, c::IP_TOS)?; + Ok(value as u8) +} + +#[inline] +pub(crate) fn set_ip_recvtos(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS, from_bool(value)) +} + +#[inline] +pub(crate) fn ip_recvtos(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS).map(to_bool) +} + +#[inline] +pub(crate) fn set_ipv6_recvtclass(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS, from_bool(value)) +} + +#[inline] +pub(crate) fn ipv6_recvtclass(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS).map(to_bool) +} + +#[inline] +pub(crate) fn set_ip_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND, from_bool(value)) +} + +#[inline] +pub(crate) fn ip_freebind(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND).map(to_bool) +} + +#[inline] +pub(crate) fn set_ipv6_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND, from_bool(value)) +} + +#[inline] +pub(crate) fn ipv6_freebind(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND).map(to_bool) +} + +#[inline] +pub(crate) fn ip_original_dst(fd: BorrowedFd<'_>) -> io::Result { + let level = c::IPPROTO_IP; + let optname = c::SO_ORIGINAL_DST; + let mut addr = SocketAddrBuf::new(); + + getsockopt_raw(fd, level, optname, &mut addr.storage, &mut addr.len)?; + + Ok(unsafe { addr.into_any() }.try_into().unwrap()) +} + +#[inline] +pub(crate) fn ipv6_original_dst(fd: BorrowedFd<'_>) -> io::Result { + let level = c::IPPROTO_IPV6; + let optname = c::IP6T_SO_ORIGINAL_DST; + let mut addr = SocketAddrBuf::new(); + + getsockopt_raw(fd, level, optname, &mut addr.storage, &mut addr.len)?; + + Ok(unsafe { addr.into_any() }.try_into().unwrap()) +} + +#[inline] +pub(crate) fn set_ipv6_tclass(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_TCLASS, value) +} + +#[inline] +pub(crate) fn ipv6_tclass(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_TCLASS) +} + +#[inline] +pub(crate) fn set_tcp_nodelay(fd: BorrowedFd<'_>, nodelay: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY, from_bool(nodelay)) +} + +#[inline] +pub(crate) fn tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY).map(to_bool) +} + +#[inline] +pub(crate) fn set_tcp_keepcnt(fd: BorrowedFd<'_>, count: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPCNT, count) +} + +#[inline] +pub(crate) fn tcp_keepcnt(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPCNT) +} + +#[inline] +pub(crate) fn set_tcp_keepidle(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> { + let secs: c::c_uint = duration_to_secs(duration)?; + setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPIDLE, secs) +} + +#[inline] +pub(crate) fn tcp_keepidle(fd: BorrowedFd<'_>) -> io::Result { + let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPIDLE)?; + Ok(Duration::from_secs(secs.into())) +} + +#[inline] +pub(crate) fn set_tcp_keepintvl(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> { + let secs: c::c_uint = duration_to_secs(duration)?; + setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL, secs) +} + +#[inline] +pub(crate) fn tcp_keepintvl(fd: BorrowedFd<'_>) -> io::Result { + let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL)?; + Ok(Duration::from_secs(secs.into())) +} + +#[inline] +pub(crate) fn set_tcp_user_timeout(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP, c::TCP_USER_TIMEOUT, value) +} + +#[inline] +pub(crate) fn tcp_user_timeout(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_USER_TIMEOUT) +} + +#[inline] +pub(crate) fn set_tcp_quickack(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK, from_bool(value)) +} + +#[inline] +pub(crate) fn tcp_quickack(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK).map(to_bool) +} + +#[inline] +pub(crate) fn set_tcp_congestion(fd: BorrowedFd<'_>, value: &str) -> io::Result<()> { + let level = c::IPPROTO_TCP; + let optname = c::TCP_CONGESTION; + let optlen = value.len().try_into().unwrap(); + setsockopt_raw(fd, level, optname, value.as_ptr(), optlen) +} + +#[cfg(feature = "alloc")] +#[inline] +pub(crate) fn tcp_congestion(fd: BorrowedFd<'_>) -> io::Result { + const OPTLEN: c::socklen_t = 16; + + let level = c::IPPROTO_TCP; + let optname = c::TCP_CONGESTION; + let mut value = MaybeUninit::<[MaybeUninit; OPTLEN as usize]>::uninit(); + let mut optlen = OPTLEN; + getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; + unsafe { + let value = value.assume_init(); + let slice: &[u8] = core::mem::transmute(&value[..optlen as usize]); + assert!(slice.contains(&b'\0')); + Ok( + core::str::from_utf8(CStr::from_ptr(slice.as_ptr().cast()).to_bytes()) + .unwrap() + .to_owned(), + ) + } +} + +#[inline] +pub(crate) fn set_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt( + fd, + c::IPPROTO_TCP, + c::TCP_THIN_LINEAR_TIMEOUTS, + from_bool(value), + ) +} + +#[inline] +pub(crate) fn tcp_thin_linear_timeouts(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_THIN_LINEAR_TIMEOUTS).map(to_bool) +} + +#[inline] +pub(crate) fn set_tcp_cork(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK, from_bool(value)) +} + +#[inline] +pub(crate) fn tcp_cork(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK).map(to_bool) +} + +#[inline] +pub(crate) fn socket_peercred(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, linux_raw_sys::net::SO_PEERCRED) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn set_xdp_umem_reg(fd: BorrowedFd<'_>, value: XdpUmemReg) -> io::Result<()> { + setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_REG, value) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn set_xdp_umem_fill_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_FILL_RING, value) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn set_xdp_umem_completion_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_COMPLETION_RING, value) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn set_xdp_tx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::SOL_XDP, c::XDP_TX_RING, value) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn set_xdp_rx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::SOL_XDP, c::XDP_RX_RING, value) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn xdp_mmap_offsets(fd: BorrowedFd<'_>) -> io::Result { + // The kernel will write `xdp_mmap_offsets` or `xdp_mmap_offsets_v1` to the + // supplied pointer, depending on the kernel version. Both structs only + // contain u64 values. By using the larger of both as the parameter, we can + // shuffle the values to the non-v1 version returned by + // `get_xdp_mmap_offsets` while keeping the return type unaffected by the + // kernel version. This works because C will layout all struct members one + // after the other. + + let mut optlen = size_of::().try_into().unwrap(); + debug_assert!( + optlen as usize >= size_of::(), + "Socket APIs don't ever use `bool` directly" + ); + let mut value = MaybeUninit::::zeroed(); + getsockopt_raw(fd, c::SOL_XDP, c::XDP_MMAP_OFFSETS, &mut value, &mut optlen)?; + + if optlen as usize == size_of::() { + // SAFETY: All members of xdp_mmap_offsets are `u64` and thus are + // correctly initialized by `MaybeUninit::::zeroed()`. + let xpd_mmap_offsets = unsafe { value.assume_init() }; + Ok(XdpMmapOffsets { + rx: XdpRingOffset { + producer: xpd_mmap_offsets.rx.producer, + consumer: xpd_mmap_offsets.rx.consumer, + desc: xpd_mmap_offsets.rx.desc, + flags: None, + }, + tx: XdpRingOffset { + producer: xpd_mmap_offsets.rx.flags, + consumer: xpd_mmap_offsets.tx.producer, + desc: xpd_mmap_offsets.tx.consumer, + flags: None, + }, + fr: XdpRingOffset { + producer: xpd_mmap_offsets.tx.desc, + consumer: xpd_mmap_offsets.tx.flags, + desc: xpd_mmap_offsets.fr.producer, + flags: None, + }, + cr: XdpRingOffset { + producer: xpd_mmap_offsets.fr.consumer, + consumer: xpd_mmap_offsets.fr.desc, + desc: xpd_mmap_offsets.fr.flags, + flags: None, + }, + }) + } else { + assert_eq!( + optlen as usize, + size_of::(), + "unexpected getsockopt size" + ); + // SAFETY: All members of xdp_mmap_offsets are `u64` and thus are + // correctly initialized by `MaybeUninit::::zeroed()`. + let xpd_mmap_offsets = unsafe { value.assume_init() }; + Ok(XdpMmapOffsets { + rx: XdpRingOffset { + producer: xpd_mmap_offsets.rx.producer, + consumer: xpd_mmap_offsets.rx.consumer, + desc: xpd_mmap_offsets.rx.desc, + flags: Some(xpd_mmap_offsets.rx.flags), + }, + tx: XdpRingOffset { + producer: xpd_mmap_offsets.tx.producer, + consumer: xpd_mmap_offsets.tx.consumer, + desc: xpd_mmap_offsets.tx.desc, + flags: Some(xpd_mmap_offsets.tx.flags), + }, + fr: XdpRingOffset { + producer: xpd_mmap_offsets.fr.producer, + consumer: xpd_mmap_offsets.fr.consumer, + desc: xpd_mmap_offsets.fr.desc, + flags: Some(xpd_mmap_offsets.fr.flags), + }, + cr: XdpRingOffset { + producer: xpd_mmap_offsets.cr.producer, + consumer: xpd_mmap_offsets.cr.consumer, + desc: xpd_mmap_offsets.cr.desc, + flags: Some(xpd_mmap_offsets.cr.flags), + }, + }) + } +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn xdp_statistics(fd: BorrowedFd<'_>) -> io::Result { + let mut optlen = size_of::().try_into().unwrap(); + debug_assert!( + optlen as usize >= size_of::(), + "Socket APIs don't ever use `bool` directly" + ); + let mut value = MaybeUninit::::zeroed(); + getsockopt_raw(fd, c::SOL_XDP, c::XDP_STATISTICS, &mut value, &mut optlen)?; + + if optlen as usize == size_of::() { + // SAFETY: All members of xdp_statistics are `u64` and thus are + // correctly initialized by `MaybeUninit::::zeroed()`. + let xdp_statistics = unsafe { value.assume_init() }; + Ok(XdpStatistics { + rx_dropped: xdp_statistics.rx_dropped, + rx_invalid_descs: xdp_statistics.rx_dropped, + tx_invalid_descs: xdp_statistics.rx_dropped, + rx_ring_full: None, + rx_fill_ring_empty_descs: None, + tx_ring_empty_descs: None, + }) + } else { + assert_eq!( + optlen as usize, + size_of::(), + "unexpected getsockopt size" + ); + // SAFETY: All members of xdp_statistics are `u64` and thus are + // correctly initialized by `MaybeUninit::::zeroed()`. + let xdp_statistics = unsafe { value.assume_init() }; + Ok(XdpStatistics { + rx_dropped: xdp_statistics.rx_dropped, + rx_invalid_descs: xdp_statistics.rx_invalid_descs, + tx_invalid_descs: xdp_statistics.tx_invalid_descs, + rx_ring_full: Some(xdp_statistics.rx_ring_full), + rx_fill_ring_empty_descs: Some(xdp_statistics.rx_fill_ring_empty_descs), + tx_ring_empty_descs: Some(xdp_statistics.tx_ring_empty_descs), + }) + } +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn xdp_options(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_XDP, c::XDP_OPTIONS) +} + +#[inline] +fn from_in_addr(in_addr: c::in_addr) -> Ipv4Addr { + Ipv4Addr::from(in_addr.s_addr.to_ne_bytes()) +} + +#[inline] +fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq { + c::ip_mreq { + imr_multiaddr: to_imr_addr(multiaddr), + imr_interface: to_imr_addr(interface), + } +} + +#[inline] +fn to_ip_mreqn(multiaddr: &Ipv4Addr, address: &Ipv4Addr, ifindex: i32) -> c::ip_mreqn { + c::ip_mreqn { + imr_multiaddr: to_imr_addr(multiaddr), + imr_address: to_imr_addr(address), + imr_ifindex: ifindex, + } +} + +#[inline] +fn to_imr_source( + multiaddr: &Ipv4Addr, + interface: &Ipv4Addr, + sourceaddr: &Ipv4Addr, +) -> c::ip_mreq_source { + c::ip_mreq_source { + imr_multiaddr: to_imr_addr(multiaddr).s_addr, + imr_interface: to_imr_addr(interface).s_addr, + imr_sourceaddr: to_imr_addr(sourceaddr).s_addr, + } +} + +#[inline] +fn to_imr_addr(addr: &Ipv4Addr) -> c::in_addr { + c::in_addr { + s_addr: u32::from_ne_bytes(addr.octets()), + } +} + +#[inline] +fn to_ipv6mr(multiaddr: &Ipv6Addr, interface: u32) -> c::ipv6_mreq { + c::ipv6_mreq { + ipv6mr_multiaddr: to_ipv6mr_multiaddr(multiaddr), + ipv6mr_ifindex: to_ipv6mr_interface(interface), + } +} + +#[inline] +fn to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr { + c::in6_addr { + in6_u: linux_raw_sys::net::in6_addr__bindgen_ty_1 { + u6_addr8: multiaddr.octets(), + }, + } +} + +#[inline] +fn to_ipv6mr_interface(interface: u32) -> c::c_int { + interface as c::c_int +} + +#[inline] +fn from_bool(value: bool) -> c::c_uint { + c::c_uint::from(value) +} + +#[inline] +fn to_bool(value: c::c_uint) -> bool { + value != 0 +} + +/// Convert to seconds, rounding up if necessary. +#[inline] +fn duration_to_secs>(duration: Duration) -> io::Result { + let mut secs = duration.as_secs(); + if duration.subsec_nanos() != 0 { + secs = secs.checked_add(1).ok_or(io::Errno::INVAL)?; + } + T::try_from(secs).map_err(|_e| io::Errno::INVAL) +} diff --git a/vendor/rustix/src/backend/linux_raw/net/syscalls.rs b/vendor/rustix/src/backend/linux_raw/net/syscalls.rs new file mode 100644 index 00000000..488e08f0 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/net/syscalls.rs @@ -0,0 +1,731 @@ +//! linux_raw syscalls supporting `rustix::net`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + +use super::msghdr::{noaddr_msghdr, with_msghdr, with_recv_msghdr}; +use super::read_sockaddr::initialize_family_to_unspec; +use super::send_recv::{RecvFlags, ReturnFlags, SendFlags}; +use crate::backend::c; +#[cfg(target_os = "linux")] +use crate::backend::conv::slice_mut; +use crate::backend::conv::{ + by_mut, by_ref, c_int, c_uint, pass_usize, ret, ret_owned_fd, ret_usize, size_of, slice, + socklen_t, zero, +}; +use crate::backend::reg::raw_arg; +use crate::fd::{BorrowedFd, OwnedFd}; +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::net::addr::SocketAddrArg; +#[cfg(target_os = "linux")] +use crate::net::MMsgHdr; +use crate::net::{ + AddressFamily, Protocol, RecvAncillaryBuffer, RecvMsg, SendAncillaryBuffer, Shutdown, + SocketAddrAny, SocketAddrBuf, SocketFlags, SocketType, +}; +use core::mem::MaybeUninit; +#[cfg(target_arch = "x86")] +use { + crate::backend::conv::{slice_just_addr, x86_sys}, + crate::backend::reg::{ArgReg, SocketArg}, + linux_raw_sys::net::{ + SYS_ACCEPT, SYS_ACCEPT4, SYS_BIND, SYS_CONNECT, SYS_GETPEERNAME, SYS_GETSOCKNAME, + SYS_LISTEN, SYS_RECV, SYS_RECVFROM, SYS_RECVMSG, SYS_SEND, SYS_SENDMMSG, SYS_SENDMSG, + SYS_SENDTO, SYS_SHUTDOWN, SYS_SOCKET, SYS_SOCKETPAIR, + }, +}; + +#[inline] +pub(crate) fn socket( + family: AddressFamily, + type_: SocketType, + protocol: Option, +) -> io::Result { + #[cfg(not(target_arch = "x86"))] + unsafe { + ret_owned_fd(syscall_readonly!(__NR_socket, family, type_, protocol)) + } + #[cfg(target_arch = "x86")] + unsafe { + ret_owned_fd(syscall_readonly!( + __NR_socketcall, + x86_sys(SYS_SOCKET), + slice_just_addr::, _>(&[ + family.into(), + type_.into(), + protocol.into(), + ]) + )) + } +} + +#[inline] +pub(crate) fn socket_with( + family: AddressFamily, + type_: SocketType, + flags: SocketFlags, + protocol: Option, +) -> io::Result { + #[cfg(not(target_arch = "x86"))] + unsafe { + ret_owned_fd(syscall_readonly!( + __NR_socket, + family, + (type_, flags), + protocol + )) + } + #[cfg(target_arch = "x86")] + unsafe { + ret_owned_fd(syscall_readonly!( + __NR_socketcall, + x86_sys(SYS_SOCKET), + slice_just_addr::, _>(&[ + family.into(), + (type_, flags).into(), + protocol.into(), + ]) + )) + } +} + +#[inline] +pub(crate) fn socketpair( + family: AddressFamily, + type_: SocketType, + flags: SocketFlags, + protocol: Option, +) -> io::Result<(OwnedFd, OwnedFd)> { + #[cfg(not(target_arch = "x86"))] + unsafe { + let mut result = MaybeUninit::<[OwnedFd; 2]>::uninit(); + ret(syscall!( + __NR_socketpair, + family, + (type_, flags), + protocol, + &mut result + ))?; + let [fd0, fd1] = result.assume_init(); + Ok((fd0, fd1)) + } + #[cfg(target_arch = "x86")] + unsafe { + let mut result = MaybeUninit::<[OwnedFd; 2]>::uninit(); + ret(syscall!( + __NR_socketcall, + x86_sys(SYS_SOCKETPAIR), + slice_just_addr::, _>(&[ + family.into(), + (type_, flags).into(), + protocol.into(), + (&mut result).into(), + ]) + ))?; + let [fd0, fd1] = result.assume_init(); + Ok((fd0, fd1)) + } +} + +#[inline] +pub(crate) fn accept(fd: BorrowedFd<'_>) -> io::Result { + #[cfg(not(any(target_arch = "x86", target_arch = "s390x")))] + unsafe { + let fd = ret_owned_fd(syscall_readonly!(__NR_accept, fd, zero(), zero()))?; + Ok(fd) + } + #[cfg(target_arch = "x86")] + unsafe { + let fd = ret_owned_fd(syscall_readonly!( + __NR_socketcall, + x86_sys(SYS_ACCEPT), + slice_just_addr::, _>(&[fd.into(), zero(), zero()]) + ))?; + Ok(fd) + } + #[cfg(target_arch = "s390x")] + { + // accept is not available on s390x + accept_with(fd, SocketFlags::empty()) + } +} + +#[inline] +pub(crate) fn accept_with(fd: BorrowedFd<'_>, flags: SocketFlags) -> io::Result { + #[cfg(not(target_arch = "x86"))] + unsafe { + let fd = ret_owned_fd(syscall_readonly!(__NR_accept4, fd, zero(), zero(), flags))?; + Ok(fd) + } + #[cfg(target_arch = "x86")] + unsafe { + let fd = ret_owned_fd(syscall_readonly!( + __NR_socketcall, + x86_sys(SYS_ACCEPT4), + slice_just_addr::, _>(&[fd.into(), zero(), zero(), flags.into()]) + ))?; + Ok(fd) + } +} + +#[inline] +pub(crate) fn acceptfrom(fd: BorrowedFd<'_>) -> io::Result<(OwnedFd, Option)> { + #[cfg(not(any(target_arch = "x86", target_arch = "s390x")))] + unsafe { + let mut addr = SocketAddrBuf::new(); + let fd = ret_owned_fd(syscall!( + __NR_accept, + fd, + &mut addr.storage, + by_mut(&mut addr.len) + ))?; + Ok((fd, addr.into_any_option())) + } + #[cfg(target_arch = "x86")] + unsafe { + let mut addr = SocketAddrBuf::new(); + let fd = ret_owned_fd(syscall!( + __NR_socketcall, + x86_sys(SYS_ACCEPT), + slice_just_addr::, _>(&[ + fd.into(), + (&mut addr.storage).into(), + by_mut(&mut addr.len), + ]) + ))?; + Ok((fd, addr.into_any_option())) + } + #[cfg(target_arch = "s390x")] + { + // accept is not available on s390x + acceptfrom_with(fd, SocketFlags::empty()) + } +} + +#[inline] +pub(crate) fn acceptfrom_with( + fd: BorrowedFd<'_>, + flags: SocketFlags, +) -> io::Result<(OwnedFd, Option)> { + #[cfg(not(target_arch = "x86"))] + unsafe { + let mut addr = SocketAddrBuf::new(); + let fd = ret_owned_fd(syscall!( + __NR_accept4, + fd, + &mut addr.storage, + by_mut(&mut addr.len), + flags + ))?; + Ok((fd, addr.into_any_option())) + } + #[cfg(target_arch = "x86")] + unsafe { + let mut addr = SocketAddrBuf::new(); + let fd = ret_owned_fd(syscall!( + __NR_socketcall, + x86_sys(SYS_ACCEPT4), + slice_just_addr::, _>(&[ + fd.into(), + (&mut addr.storage).into(), + by_mut(&mut addr.len), + flags.into(), + ]) + ))?; + Ok((fd, addr.into_any_option())) + } +} + +#[inline] +pub(crate) fn recvmsg( + sockfd: BorrowedFd<'_>, + iov: &mut [IoSliceMut<'_>], + control: &mut RecvAncillaryBuffer<'_>, + msg_flags: RecvFlags, +) -> io::Result { + 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| { + #[cfg(not(target_arch = "x86"))] + let result = ret_usize(syscall!(__NR_recvmsg, sockfd, by_mut(msghdr), msg_flags)); + + #[cfg(target_arch = "x86")] + let result = ret_usize(syscall!( + __NR_socketcall, + x86_sys(SYS_RECVMSG), + slice_just_addr::, _>(&[ + sockfd.into(), + by_mut(msghdr), + msg_flags.into(), + ]) + )); + + result.map(|bytes| (bytes, msghdr.msg_flags)) + })? + }; + + // Get the address of the sender, if any. + Ok(RecvMsg { + bytes, + address: unsafe { addr.into_any_option() }, + flags: ReturnFlags::from_bits_retain(flags), + }) +} + +#[inline] +pub(crate) fn sendmsg( + sockfd: BorrowedFd<'_>, + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + msg_flags: SendFlags, +) -> io::Result { + let msghdr = noaddr_msghdr(iov, control); + + #[cfg(not(target_arch = "x86"))] + let result = unsafe { ret_usize(syscall!(__NR_sendmsg, sockfd, by_ref(&msghdr), msg_flags)) }; + + #[cfg(target_arch = "x86")] + let result = unsafe { + ret_usize(syscall!( + __NR_socketcall, + x86_sys(SYS_SENDMSG), + slice_just_addr::, _>(&[ + sockfd.into(), + by_ref(&msghdr), + msg_flags.into() + ]) + )) + }; + + result +} + +#[inline] +pub(crate) fn sendmsg_addr( + sockfd: BorrowedFd<'_>, + addr: &impl SocketAddrArg, + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + msg_flags: SendFlags, +) -> io::Result { + // 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| { + #[cfg(not(target_arch = "x86"))] + let result = ret_usize(syscall!(__NR_sendmsg, sockfd, by_ref(msghdr), msg_flags)); + + #[cfg(target_arch = "x86")] + let result = ret_usize(syscall!( + __NR_socketcall, + x86_sys(SYS_SENDMSG), + slice_just_addr::, _>(&[ + sockfd.into(), + by_ref(msghdr), + msg_flags.into(), + ]) + )); + + result + }) + } +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn sendmmsg( + sockfd: BorrowedFd<'_>, + msgs: &mut [MMsgHdr<'_>], + flags: SendFlags, +) -> io::Result { + let (msgs, len) = slice_mut(msgs); + + #[cfg(not(target_arch = "x86"))] + let result = unsafe { ret_usize(syscall!(__NR_sendmmsg, sockfd, msgs, len, flags)) }; + + #[cfg(target_arch = "x86")] + let result = unsafe { + ret_usize(syscall!( + __NR_socketcall, + x86_sys(SYS_SENDMMSG), + slice_just_addr::, _>(&[sockfd.into(), msgs, len, flags.into()]) + )) + }; + + result +} + +#[inline] +pub(crate) fn shutdown(fd: BorrowedFd<'_>, how: Shutdown) -> io::Result<()> { + #[cfg(not(target_arch = "x86"))] + unsafe { + ret(syscall_readonly!( + __NR_shutdown, + fd, + c_uint(how as c::c_uint) + )) + } + #[cfg(target_arch = "x86")] + unsafe { + ret(syscall_readonly!( + __NR_socketcall, + x86_sys(SYS_SHUTDOWN), + slice_just_addr::, _>(&[fd.into(), c_uint(how as c::c_uint)]) + )) + } +} + +#[inline] +pub(crate) fn send(fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags) -> io::Result { + let (buf_addr, buf_len) = slice(buf); + + #[cfg(not(any( + target_arch = "aarch64", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "x86", + target_arch = "x86_64", + )))] + unsafe { + ret_usize(syscall_readonly!(__NR_send, fd, buf_addr, buf_len, flags)) + } + #[cfg(any( + target_arch = "aarch64", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "x86_64", + ))] + unsafe { + ret_usize(syscall_readonly!( + __NR_sendto, + fd, + buf_addr, + buf_len, + flags, + zero(), + zero() + )) + } + #[cfg(target_arch = "x86")] + unsafe { + ret_usize(syscall_readonly!( + __NR_socketcall, + x86_sys(SYS_SEND), + slice_just_addr::, _>(&[ + fd.into(), + buf_addr, + buf_len, + flags.into() + ]) + )) + } +} + +#[inline] +pub(crate) fn sendto( + fd: BorrowedFd<'_>, + buf: &[u8], + flags: SendFlags, + addr: &impl SocketAddrArg, +) -> io::Result { + let (buf_addr, buf_len) = slice(buf); + + // SAFETY: This passes the `addr_ptr` reference to the OS which reads the + // buffers only within the `addr_len` bound. + unsafe { + addr.with_sockaddr(|addr_ptr, addr_len| { + #[cfg(not(target_arch = "x86"))] + { + ret_usize(syscall_readonly!( + __NR_sendto, + fd, + buf_addr, + buf_len, + flags, + raw_arg(addr_ptr as *mut _), + socklen_t(addr_len) + )) + } + #[cfg(target_arch = "x86")] + { + ret_usize(syscall_readonly!( + __NR_socketcall, + x86_sys(SYS_SENDTO), + slice_just_addr::, _>(&[ + fd.into(), + buf_addr, + buf_len, + flags.into(), + raw_arg(addr_ptr as *mut _), + socklen_t(addr_len) + ]) + )) + } + }) + } +} + +#[inline] +pub(crate) unsafe fn recv( + fd: BorrowedFd<'_>, + buf: (*mut u8, usize), + flags: RecvFlags, +) -> io::Result { + #[cfg(not(any( + target_arch = "aarch64", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "x86", + target_arch = "x86_64", + )))] + { + ret_usize(syscall!(__NR_recv, fd, buf.0, pass_usize(buf.1), flags)) + } + #[cfg(any( + target_arch = "aarch64", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "x86_64", + ))] + { + ret_usize(syscall!( + __NR_recvfrom, + fd, + buf.0, + pass_usize(buf.1), + flags, + zero(), + zero() + )) + } + #[cfg(target_arch = "x86")] + { + ret_usize(syscall!( + __NR_socketcall, + x86_sys(SYS_RECV), + slice_just_addr::, _>(&[ + fd.into(), + buf.0.into(), + pass_usize(buf.1), + flags.into(), + ]) + )) + } +} + +#[inline] +pub(crate) unsafe fn recvfrom( + fd: BorrowedFd<'_>, + buf: (*mut u8, usize), + flags: RecvFlags, +) -> io::Result<(usize, Option)> { + 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::()); + + #[cfg(not(target_arch = "x86"))] + let nread = ret_usize(syscall!( + __NR_recvfrom, + fd, + buf.0, + pass_usize(buf.1), + flags, + &mut addr.storage, + by_mut(&mut addr.len) + ))?; + #[cfg(target_arch = "x86")] + let nread = ret_usize(syscall!( + __NR_socketcall, + x86_sys(SYS_RECVFROM), + slice_just_addr::, _>(&[ + fd.into(), + buf.0.into(), + pass_usize(buf.1), + flags.into(), + (&mut addr.storage).into(), + by_mut(&mut addr.len), + ]) + ))?; + + Ok((nread, addr.into_any_option())) +} + +#[inline] +pub(crate) fn getpeername(fd: BorrowedFd<'_>) -> io::Result> { + #[cfg(not(target_arch = "x86"))] + unsafe { + let mut addr = SocketAddrBuf::new(); + ret(syscall!( + __NR_getpeername, + fd, + &mut addr.storage, + by_mut(&mut addr.len) + ))?; + Ok(addr.into_any_option()) + } + #[cfg(target_arch = "x86")] + unsafe { + let mut addr = SocketAddrBuf::new(); + ret(syscall!( + __NR_socketcall, + x86_sys(SYS_GETPEERNAME), + slice_just_addr::, _>(&[ + fd.into(), + (&mut addr.storage).into(), + by_mut(&mut addr.len), + ]) + ))?; + Ok(addr.into_any_option()) + } +} + +#[inline] +pub(crate) fn getsockname(fd: BorrowedFd<'_>) -> io::Result { + #[cfg(not(target_arch = "x86"))] + unsafe { + let mut addr = SocketAddrBuf::new(); + ret(syscall!( + __NR_getsockname, + fd, + &mut addr.storage, + by_mut(&mut addr.len) + ))?; + Ok(addr.into_any()) + } + #[cfg(target_arch = "x86")] + unsafe { + let mut addr = SocketAddrBuf::new(); + ret(syscall!( + __NR_socketcall, + x86_sys(SYS_GETSOCKNAME), + slice_just_addr::, _>(&[ + fd.into(), + (&mut addr.storage).into(), + by_mut(&mut addr.len), + ]) + ))?; + Ok(addr.into_any()) + } +} + +#[inline] +pub(crate) fn bind(fd: BorrowedFd<'_>, addr: &impl SocketAddrArg) -> io::Result<()> { + // SAFETY: This passes the `addr_ptr` reference to the OS which reads the + // buffers only within the `addr_len` bound. + unsafe { + addr.with_sockaddr(|addr_ptr, addr_len| { + #[cfg(not(target_arch = "x86"))] + { + ret(syscall_readonly!( + __NR_bind, + fd, + raw_arg(addr_ptr as *mut _), + socklen_t(addr_len) + )) + } + #[cfg(target_arch = "x86")] + { + ret(syscall_readonly!( + __NR_socketcall, + x86_sys(SYS_BIND), + slice_just_addr::, _>(&[ + fd.into(), + raw_arg(addr_ptr as *mut _), + socklen_t(addr_len) + ]) + )) + } + }) + } +} + +#[inline] +pub(crate) fn connect(fd: BorrowedFd<'_>, addr: &impl SocketAddrArg) -> io::Result<()> { + // SAFETY: This passes the `addr_ptr` reference to the OS which reads the + // buffers only within the `addr_len` bound. + unsafe { + addr.with_sockaddr(|addr_ptr, addr_len| { + #[cfg(not(target_arch = "x86"))] + { + ret(syscall_readonly!( + __NR_connect, + fd, + raw_arg(addr_ptr as *mut _), + socklen_t(addr_len) + )) + } + #[cfg(target_arch = "x86")] + { + ret(syscall_readonly!( + __NR_socketcall, + x86_sys(SYS_CONNECT), + slice_just_addr::, _>(&[ + fd.into(), + raw_arg(addr_ptr as *mut _), + socklen_t(addr_len) + ]) + )) + } + }) + } +} + +#[inline] +pub(crate) fn connect_unspec(fd: BorrowedFd<'_>) -> io::Result<()> { + debug_assert_eq!(c::AF_UNSPEC, 0); + let addr = MaybeUninit::::zeroed(); + + #[cfg(not(target_arch = "x86"))] + unsafe { + ret(syscall_readonly!( + __NR_connect, + fd, + by_ref(&addr), + size_of::() + )) + } + #[cfg(target_arch = "x86")] + unsafe { + ret(syscall_readonly!( + __NR_socketcall, + x86_sys(SYS_CONNECT), + slice_just_addr::, _>(&[ + fd.into(), + by_ref(&addr), + size_of::(), + ]) + )) + } +} + +#[inline] +pub(crate) fn listen(fd: BorrowedFd<'_>, backlog: c::c_int) -> io::Result<()> { + #[cfg(not(target_arch = "x86"))] + unsafe { + ret(syscall_readonly!(__NR_listen, fd, c_int(backlog))) + } + #[cfg(target_arch = "x86")] + unsafe { + ret(syscall_readonly!( + __NR_socketcall, + x86_sys(SYS_LISTEN), + slice_just_addr::, _>(&[fd.into(), c_int(backlog)]) + )) + } +} diff --git a/vendor/rustix/src/backend/linux_raw/net/write_sockaddr.rs b/vendor/rustix/src/backend/linux_raw/net/write_sockaddr.rs new file mode 100644 index 00000000..3b348221 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/net/write_sockaddr.rs @@ -0,0 +1,31 @@ +//! 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. +#![allow(unsafe_code)] + +use crate::backend::c; +use crate::net::{SocketAddrV4, SocketAddrV6}; + +pub(crate) fn encode_sockaddr_v4(v4: &SocketAddrV4) -> c::sockaddr_in { + c::sockaddr_in { + sin_family: c::AF_INET as _, + sin_port: u16::to_be(v4.port()), + sin_addr: c::in_addr { + s_addr: u32::from_ne_bytes(v4.ip().octets()), + }, + __pad: [0_u8; 8], + } +} + +pub(crate) fn encode_sockaddr_v6(v6: &SocketAddrV6) -> c::sockaddr_in6 { + c::sockaddr_in6 { + sin6_family: c::AF_INET6 as _, + sin6_port: u16::to_be(v6.port()), + sin6_flowinfo: u32::to_be(v6.flowinfo()), + sin6_addr: c::in6_addr { + in6_u: linux_raw_sys::net::in6_addr__bindgen_ty_1 { + u6_addr8: v6.ip().octets(), + }, + }, + sin6_scope_id: v6.scope_id(), + } +} diff --git a/vendor/rustix/src/backend/linux_raw/param/auxv.rs b/vendor/rustix/src/backend/linux_raw/param/auxv.rs new file mode 100644 index 00000000..d748219a --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/param/auxv.rs @@ -0,0 +1,583 @@ +//! Linux auxv support. +//! +//! # Safety +//! +//! This uses raw pointers to locate and read the kernel-provided auxv array. +#![allow(unsafe_code)] + +use super::super::conv::{c_int, pass_usize, ret_usize}; +use crate::backend::c; +use crate::fd::OwnedFd; +#[cfg(feature = "param")] +use crate::ffi::CStr; +use crate::fs::{Mode, OFlags}; +use crate::utils::{as_ptr, check_raw_pointer}; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; +use core::mem::size_of; +use core::ptr::{null_mut, read_unaligned, NonNull}; +#[cfg(feature = "runtime")] +use core::sync::atomic::AtomicU8; +use core::sync::atomic::Ordering::Relaxed; +use core::sync::atomic::{AtomicPtr, AtomicUsize}; +use linux_raw_sys::elf::*; +use linux_raw_sys::general::{ + AT_BASE, AT_CLKTCK, AT_EXECFN, AT_HWCAP, AT_HWCAP2, AT_MINSIGSTKSZ, AT_NULL, AT_PAGESZ, + AT_SYSINFO_EHDR, +}; +#[cfg(feature = "runtime")] +use linux_raw_sys::general::{ + AT_EGID, AT_ENTRY, AT_EUID, AT_GID, AT_PHDR, AT_PHENT, AT_PHNUM, AT_RANDOM, AT_SECURE, AT_UID, +}; +#[cfg(feature = "alloc")] +use {alloc::borrow::Cow, alloc::vec}; + +#[cfg(feature = "param")] +#[inline] +pub(crate) fn page_size() -> usize { + let mut page_size = PAGE_SIZE.load(Relaxed); + + if page_size == 0 { + init_auxv(); + page_size = PAGE_SIZE.load(Relaxed); + } + + page_size +} + +#[cfg(feature = "param")] +#[inline] +pub(crate) fn clock_ticks_per_second() -> u64 { + let mut ticks = CLOCK_TICKS_PER_SECOND.load(Relaxed); + + if ticks == 0 { + init_auxv(); + ticks = CLOCK_TICKS_PER_SECOND.load(Relaxed); + } + + ticks as u64 +} + +#[cfg(feature = "param")] +#[inline] +pub(crate) fn linux_hwcap() -> (usize, usize) { + let mut hwcap = HWCAP.load(Relaxed); + let mut hwcap2 = HWCAP2.load(Relaxed); + + if hwcap == 0 || hwcap2 == 0 { + init_auxv(); + hwcap = HWCAP.load(Relaxed); + hwcap2 = HWCAP2.load(Relaxed); + } + + (hwcap, hwcap2) +} + +#[cfg(feature = "param")] +#[inline] +pub(crate) fn linux_minsigstksz() -> usize { + let mut minsigstksz = MINSIGSTKSZ.load(Relaxed); + + if minsigstksz == 0 { + init_auxv(); + minsigstksz = MINSIGSTKSZ.load(Relaxed); + } + + minsigstksz +} + +#[cfg(feature = "param")] +#[inline] +pub(crate) fn linux_execfn() -> &'static CStr { + let mut execfn = EXECFN.load(Relaxed); + + if execfn.is_null() { + init_auxv(); + execfn = EXECFN.load(Relaxed); + } + + // SAFETY: We assume the `AT_EXECFN` value provided by the kernel is a + // valid pointer to a valid NUL-terminated array of bytes. + unsafe { CStr::from_ptr(execfn.cast()) } +} + +#[cfg(feature = "runtime")] +#[inline] +pub(crate) fn linux_secure() -> bool { + let mut secure = SECURE.load(Relaxed); + + // 0 means not initialized yet. + if secure == 0 { + init_auxv(); + secure = SECURE.load(Relaxed); + } + + // 0 means not present. Libc `getauxval(AT_SECURE)` would return 0. + // 1 means not in secure mode. + // 2 means in secure mode. + secure > 1 +} + +#[cfg(feature = "runtime")] +#[inline] +pub(crate) fn exe_phdrs() -> (*const c::c_void, usize, usize) { + let mut phdr = PHDR.load(Relaxed); + let mut phent = PHENT.load(Relaxed); + let mut phnum = PHNUM.load(Relaxed); + + if phdr.is_null() || phnum == 0 { + init_auxv(); + phdr = PHDR.load(Relaxed); + phent = PHENT.load(Relaxed); + phnum = PHNUM.load(Relaxed); + } + + (phdr.cast(), phent, phnum) +} + +/// `AT_SYSINFO_EHDR` isn't present on all platforms in all configurations, so +/// if we don't see it, this function returns a null pointer. +/// +/// And, this function returns a null pointer, rather than panicking, if the +/// auxv records can't be read. +#[inline] +pub(in super::super) fn sysinfo_ehdr() -> *const Elf_Ehdr { + let mut ehdr = SYSINFO_EHDR.load(Relaxed); + + if ehdr.is_null() { + // Use `maybe_init_auxv` to read the aux vectors if it can, but do + // nothing if it can't. If it can't, then we'll get a null pointer + // here, which our callers are prepared to deal with. + maybe_init_auxv(); + + ehdr = SYSINFO_EHDR.load(Relaxed); + } + + ehdr +} + +#[cfg(feature = "runtime")] +#[inline] +pub(crate) fn entry() -> usize { + let mut entry = ENTRY.load(Relaxed); + + if entry == 0 { + init_auxv(); + entry = ENTRY.load(Relaxed); + } + + entry +} + +#[cfg(feature = "runtime")] +#[inline] +pub(crate) fn random() -> *const [u8; 16] { + let mut random = RANDOM.load(Relaxed); + + if random.is_null() { + init_auxv(); + random = RANDOM.load(Relaxed); + } + + random +} + +static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); +static CLOCK_TICKS_PER_SECOND: AtomicUsize = AtomicUsize::new(0); +static HWCAP: AtomicUsize = AtomicUsize::new(0); +static HWCAP2: AtomicUsize = AtomicUsize::new(0); +static MINSIGSTKSZ: AtomicUsize = AtomicUsize::new(0); +static EXECFN: AtomicPtr = AtomicPtr::new(null_mut()); +static SYSINFO_EHDR: AtomicPtr = AtomicPtr::new(null_mut()); +#[cfg(feature = "runtime")] +static SECURE: AtomicU8 = AtomicU8::new(0); +#[cfg(feature = "runtime")] +static PHDR: AtomicPtr = AtomicPtr::new(null_mut()); +#[cfg(feature = "runtime")] +static PHENT: AtomicUsize = AtomicUsize::new(0); +#[cfg(feature = "runtime")] +static PHNUM: AtomicUsize = AtomicUsize::new(0); +#[cfg(feature = "runtime")] +static ENTRY: AtomicUsize = AtomicUsize::new(0); +#[cfg(feature = "runtime")] +static RANDOM: AtomicPtr<[u8; 16]> = AtomicPtr::new(null_mut()); + +const PR_GET_AUXV: c::c_int = 0x4155_5856; + +/// Use Linux ≥ 6.4's [`PR_GET_AUXV`] to read the aux records, into a provided +/// statically-sized buffer. Return: +/// - `Ok(…)` if the buffer is big enough. +/// - `Err(Ok(len))` if we need a buffer of length `len`. +/// - `Err(Err(err))` if we failed with `err`. +/// +/// [`PR_GET_AUXV`]: https://www.man7.org/linux/man-pages/man2/PR_GET_AUXV.2const.html +#[cold] +fn pr_get_auxv_static(buffer: &mut [u8; 512]) -> Result<&mut [u8], crate::io::Result> { + let len = unsafe { + ret_usize(syscall_always_asm!( + __NR_prctl, + c_int(PR_GET_AUXV), + buffer.as_mut_ptr(), + pass_usize(buffer.len()), + pass_usize(0), + pass_usize(0) + )) + .map_err(Err)? + }; + if len <= buffer.len() { + return Ok(&mut buffer[..len]); + } + Err(Ok(len)) +} + +/// Use Linux ≥ 6.4's [`PR_GET_AUXV`] to read the aux records, using a +/// provided statically-sized buffer if possible, or a dynamically allocated +/// buffer otherwise. Return: +/// - Ok(…) on success. +/// - Err(err) on failure. +/// +/// [`PR_GET_AUXV`]: https://www.man7.org/linux/man-pages/man2/PR_GET_AUXV.2const.html +#[cfg(feature = "alloc")] +#[cold] +fn pr_get_auxv_dynamic(buffer: &mut [u8; 512]) -> crate::io::Result> { + // First try use the static buffer. + let len = match pr_get_auxv_static(buffer) { + Ok(buffer) => return Ok(Cow::Borrowed(buffer)), + Err(Ok(len)) => len, + Err(Err(err)) => return Err(err), + }; + + // If that indicates it needs a bigger buffer, allocate one. + let mut buffer = vec![0_u8; len]; + let len = unsafe { + ret_usize(syscall_always_asm!( + __NR_prctl, + c_int(PR_GET_AUXV), + buffer.as_mut_ptr(), + pass_usize(buffer.len()), + pass_usize(0), + pass_usize(0) + ))? + }; + assert_eq!(len, buffer.len()); + Ok(Cow::Owned(buffer)) +} + +/// Read the auxv records and initialize the various static variables. Panic +/// if an error is encountered. +#[cold] +fn init_auxv() { + init_auxv_impl().unwrap(); +} + +/// Like `init_auxv`, but don't panic if an error is encountered. The caller +/// must be prepared for initialization to be skipped. +#[cold] +fn maybe_init_auxv() { + let _ = init_auxv_impl(); +} + +/// If we don't have "use-explicitly-provided-auxv" or "use-libc-auxv", we +/// read the aux vector via the `prctl` `PR_GET_AUXV`, with a fallback to +/// /proc/self/auxv for kernels that don't support `PR_GET_AUXV`. +#[cold] +fn init_auxv_impl() -> Result<(), ()> { + // 512 bytes of AUX elements ought to be enough for anybody… + let mut buffer = [0_u8; 512]; + + // If we don't have "alloc", just try to read into our statically-sized + // buffer. This might fail due to the buffer being insufficient; we're + // prepared to cope, though we may do suboptimal things. + #[cfg(not(feature = "alloc"))] + let result = pr_get_auxv_static(&mut buffer); + + // If we do have "alloc" then read into our statically-sized buffer if + // it fits, or fall back to a dynamically-allocated buffer. + #[cfg(feature = "alloc")] + let result = pr_get_auxv_dynamic(&mut buffer); + + if let Ok(buffer) = result { + // SAFETY: We assume the kernel returns a valid auxv. + unsafe { + init_from_aux_iter(AuxPointer(buffer.as_ptr().cast())).unwrap(); + } + return Ok(()); + } + + // If `PR_GET_AUXV` is unavailable, or if we don't have "alloc" and + // the aux records don't fit in our static buffer, then fall back to trying + // to open "/proc/self/auxv". We don't use `proc_self_fd` because its extra + // checking breaks on QEMU. + if let Ok(file) = crate::fs::open("/proc/self/auxv", OFlags::RDONLY, Mode::empty()) { + #[cfg(feature = "alloc")] + init_from_auxv_file(file).unwrap(); + + #[cfg(not(feature = "alloc"))] + unsafe { + init_from_aux_iter(AuxFile(file)).unwrap(); + } + + return Ok(()); + } + + Err(()) +} + +/// Process auxv entries from the open file `auxv`. +#[cfg(feature = "alloc")] +#[cold] +#[must_use] +fn init_from_auxv_file(auxv: OwnedFd) -> Option<()> { + let mut buffer = Vec::::with_capacity(512); + loop { + let cur = buffer.len(); + + // Request one extra byte; `Vec` will often allocate more. + buffer.reserve(1); + + // Use all the space it allocated. + buffer.resize(buffer.capacity(), 0); + + // Read up to that many bytes. + let n = match crate::io::read(&auxv, &mut buffer[cur..]) { + Err(crate::io::Errno::INTR) => 0, + Err(_err) => panic!(), + Ok(0) => break, + Ok(n) => n, + }; + + // Account for the number of bytes actually read. + buffer.resize(cur + n, 0_u8); + } + + // SAFETY: We loaded from an auxv file into the buffer. + unsafe { init_from_aux_iter(AuxPointer(buffer.as_ptr().cast())) } +} + +/// Process auxv entries from the auxv array pointed to by `auxp`. +/// +/// # Safety +/// +/// This must be passed a pointer to an auxv array. +/// +/// The buffer contains `Elf_aux_t` elements, though it need not be aligned; +/// function uses `read_unaligned` to read from it. +#[cold] +#[must_use] +unsafe fn init_from_aux_iter(aux_iter: impl Iterator) -> Option<()> { + let mut pagesz = 0; + let mut clktck = 0; + let mut hwcap = 0; + let mut hwcap2 = 0; + let mut minsigstksz = 0; + let mut execfn = null_mut(); + let mut sysinfo_ehdr = null_mut(); + #[cfg(feature = "runtime")] + let mut secure = 0; + #[cfg(feature = "runtime")] + let mut phdr = null_mut(); + #[cfg(feature = "runtime")] + let mut phnum = 0; + #[cfg(feature = "runtime")] + let mut phent = 0; + #[cfg(feature = "runtime")] + let mut entry = 0; + #[cfg(feature = "runtime")] + let mut uid = None; + #[cfg(feature = "runtime")] + let mut euid = None; + #[cfg(feature = "runtime")] + let mut gid = None; + #[cfg(feature = "runtime")] + let mut egid = None; + #[cfg(feature = "runtime")] + let mut random = null_mut(); + + for Elf_auxv_t { a_type, a_val } in aux_iter { + match a_type as _ { + AT_PAGESZ => pagesz = a_val as usize, + AT_CLKTCK => clktck = a_val as usize, + AT_HWCAP => hwcap = a_val as usize, + AT_HWCAP2 => hwcap2 = a_val as usize, + AT_MINSIGSTKSZ => minsigstksz = a_val as usize, + AT_EXECFN => execfn = check_raw_pointer::(a_val as *mut _)?.as_ptr(), + AT_SYSINFO_EHDR => sysinfo_ehdr = check_elf_base(a_val as *mut _)?.as_ptr(), + + AT_BASE => { + // The `AT_BASE` value can be null in a static executable that + // doesn't use a dynamic linker. If so, ignore it. + if !a_val.is_null() { + let _ = check_elf_base(a_val.cast())?; + } + } + + #[cfg(feature = "runtime")] + AT_SECURE => secure = (a_val as usize != 0) as u8 + 1, + #[cfg(feature = "runtime")] + AT_UID => uid = Some(a_val), + #[cfg(feature = "runtime")] + AT_EUID => euid = Some(a_val), + #[cfg(feature = "runtime")] + AT_GID => gid = Some(a_val), + #[cfg(feature = "runtime")] + AT_EGID => egid = Some(a_val), + #[cfg(feature = "runtime")] + AT_PHDR => phdr = check_raw_pointer::(a_val as *mut _)?.as_ptr(), + #[cfg(feature = "runtime")] + AT_PHNUM => phnum = a_val as usize, + #[cfg(feature = "runtime")] + AT_PHENT => phent = a_val as usize, + #[cfg(feature = "runtime")] + AT_ENTRY => entry = a_val as usize, + #[cfg(feature = "runtime")] + AT_RANDOM => random = check_raw_pointer::<[u8; 16]>(a_val as *mut _)?.as_ptr(), + + AT_NULL => break, + _ => (), + } + } + + #[cfg(feature = "runtime")] + assert_eq!(phent, size_of::()); + + // If we're running set-uid or set-gid, enable “secure execution” mode, + // which doesn't do much, but users may be depending on the things that + // it does do. + #[cfg(feature = "runtime")] + if uid != euid || gid != egid { + secure = 2; + } + + // The base and sysinfo_ehdr (if present) matches our platform. Accept the + // aux values. + PAGE_SIZE.store(pagesz, Relaxed); + CLOCK_TICKS_PER_SECOND.store(clktck, Relaxed); + HWCAP.store(hwcap, Relaxed); + HWCAP2.store(hwcap2, Relaxed); + MINSIGSTKSZ.store(minsigstksz, Relaxed); + EXECFN.store(execfn, Relaxed); + SYSINFO_EHDR.store(sysinfo_ehdr, Relaxed); + #[cfg(feature = "runtime")] + SECURE.store(secure, Relaxed); + #[cfg(feature = "runtime")] + PHDR.store(phdr, Relaxed); + #[cfg(feature = "runtime")] + PHNUM.store(phnum, Relaxed); + #[cfg(feature = "runtime")] + ENTRY.store(entry, Relaxed); + #[cfg(feature = "runtime")] + RANDOM.store(random, Relaxed); + + Some(()) +} + +/// Check that `base` is a valid pointer to the kernel-provided vDSO. +/// +/// `base` is some value we got from a `AT_SYSINFO_EHDR` aux record somewhere, +/// which hopefully holds the value of the kernel-provided vDSO in memory. Do a +/// series of checks to be as sure as we can that it's safe to use. +#[cold] +#[must_use] +unsafe fn check_elf_base(base: *const Elf_Ehdr) -> Option> { + // If we're reading a 64-bit auxv on a 32-bit platform, we'll see a zero + // `a_val` because `AT_*` values are never greater than `u32::MAX`. Zero is + // used by libc's `getauxval` to indicate errors, so it should never be a + // valid value. + if base.is_null() { + return None; + } + + let hdr = check_raw_pointer::(base as *mut _)?; + + let hdr = hdr.as_ref(); + if hdr.e_ident[..SELFMAG] != ELFMAG { + return None; // Wrong ELF magic + } + if !matches!(hdr.e_ident[EI_OSABI], ELFOSABI_SYSV | ELFOSABI_LINUX) { + return None; // Unrecognized ELF OS ABI + } + if hdr.e_ident[EI_ABIVERSION] != ELFABIVERSION { + return None; // Unrecognized ELF ABI version + } + if hdr.e_type != ET_DYN { + return None; // Wrong ELF type + } + + // If ELF is extended, we'll need to adjust. + if hdr.e_ident[EI_VERSION] != EV_CURRENT + || hdr.e_ehsize as usize != size_of::() + || hdr.e_phentsize as usize != size_of::() + { + return None; + } + // We don't currently support extra-large numbers of segments. + if hdr.e_phnum == PN_XNUM { + return None; + } + + // If `e_phoff` is zero, it's more likely that we're looking at memory that + // has been zeroed than that the kernel has somehow aliased the `Ehdr` and + // the `Phdr`. + if hdr.e_phoff < size_of::() { + return None; + } + + // Verify that the `EI_CLASS`/`EI_DATA`/`e_machine` fields match the + // architecture we're running as. This helps catch cases where we're + // running under QEMU. + if hdr.e_ident[EI_CLASS] != ELFCLASS { + return None; // Wrong ELF class + } + if hdr.e_ident[EI_DATA] != ELFDATA { + return None; // Wrong ELF data + } + if hdr.e_machine != EM_CURRENT { + return None; // Wrong machine type + } + + Some(NonNull::new_unchecked(as_ptr(hdr) as *mut _)) +} + +// Aux reading utilities + +// Read auxv records from an array in memory. +struct AuxPointer(*const Elf_auxv_t); + +impl Iterator for AuxPointer { + type Item = Elf_auxv_t; + + #[cold] + fn next(&mut self) -> Option { + unsafe { + let value = read_unaligned(self.0); + self.0 = self.0.add(1); + Some(value) + } + } +} + +// Read auxv records from a file. +#[cfg(not(feature = "alloc"))] +struct AuxFile(OwnedFd); + +#[cfg(not(feature = "alloc"))] +impl Iterator for AuxFile { + type Item = Elf_auxv_t; + + // This implementation does lots of `read`s and it isn't amazing, but + // hopefully we won't use it often. + #[cold] + fn next(&mut self) -> Option { + let mut buf = [0_u8; size_of::()]; + let mut slice = &mut buf[..]; + while !slice.is_empty() { + match crate::io::read(&self.0, &mut *slice) { + Ok(0) => panic!("unexpected end of auxv file"), + Ok(n) => slice = &mut slice[n..], + Err(crate::io::Errno::INTR) => continue, + Err(err) => panic!("{:?}", err), + } + } + Some(unsafe { read_unaligned(buf.as_ptr().cast()) }) + } +} diff --git a/vendor/rustix/src/backend/linux_raw/param/init.rs b/vendor/rustix/src/backend/linux_raw/param/init.rs new file mode 100644 index 00000000..de1e5949 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/param/init.rs @@ -0,0 +1,175 @@ +//! Linux auxv `init` function, for "use-explicitly-provided-auxv" mode. +//! +//! # Safety +//! +//! This uses raw pointers to locate and read the kernel-provided auxv array. +#![allow(unsafe_code)] + +use crate::backend::c; +#[cfg(feature = "param")] +use crate::ffi::CStr; +use core::ffi::c_void; +use core::ptr::{null_mut, read, NonNull}; +#[cfg(feature = "runtime")] +use core::sync::atomic::AtomicBool; +use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; +use linux_raw_sys::elf::*; +use linux_raw_sys::general::{ + AT_CLKTCK, AT_EXECFN, AT_HWCAP, AT_HWCAP2, AT_MINSIGSTKSZ, AT_NULL, AT_PAGESZ, AT_SYSINFO_EHDR, +}; +#[cfg(feature = "runtime")] +use linux_raw_sys::general::{AT_ENTRY, AT_PHDR, AT_PHENT, AT_PHNUM, AT_RANDOM, AT_SECURE}; + +#[cfg(feature = "param")] +#[inline] +pub(crate) fn page_size() -> usize { + PAGE_SIZE.load(Ordering::Relaxed) +} + +#[cfg(feature = "param")] +#[inline] +pub(crate) fn clock_ticks_per_second() -> u64 { + CLOCK_TICKS_PER_SECOND.load(Ordering::Relaxed) as u64 +} + +#[cfg(feature = "param")] +#[inline] +pub(crate) fn linux_hwcap() -> (usize, usize) { + ( + HWCAP.load(Ordering::Relaxed), + HWCAP2.load(Ordering::Relaxed), + ) +} + +#[cfg(feature = "param")] +#[inline] +pub(crate) fn linux_minsigstksz() -> usize { + MINSIGSTKSZ.load(Ordering::Relaxed) +} + +#[cfg(feature = "param")] +#[inline] +pub(crate) fn linux_execfn() -> &'static CStr { + let execfn = EXECFN.load(Ordering::Relaxed); + + // SAFETY: We initialize `EXECFN` to a valid `CStr` pointer, and we assume + // the `AT_EXECFN` value provided by the kernel points to a valid C string. + unsafe { CStr::from_ptr(execfn.cast()) } +} + +#[cfg(feature = "runtime")] +#[inline] +pub(crate) fn linux_secure() -> bool { + SECURE.load(Ordering::Relaxed) +} + +#[cfg(feature = "runtime")] +#[inline] +pub(crate) fn exe_phdrs() -> (*const c_void, usize, usize) { + ( + PHDR.load(Ordering::Relaxed).cast(), + PHENT.load(Ordering::Relaxed), + PHNUM.load(Ordering::Relaxed), + ) +} + +/// `AT_SYSINFO_EHDR` isn't present on all platforms in all configurations, so +/// if we don't see it, this function returns a null pointer. +#[inline] +pub(in super::super) fn sysinfo_ehdr() -> *const Elf_Ehdr { + SYSINFO_EHDR.load(Ordering::Relaxed) +} + +#[cfg(feature = "runtime")] +#[inline] +pub(crate) fn entry() -> usize { + ENTRY.load(Ordering::Relaxed) +} + +#[cfg(feature = "runtime")] +#[inline] +pub(crate) fn random() -> *const [u8; 16] { + RANDOM.load(Ordering::Relaxed) +} + +static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); +static CLOCK_TICKS_PER_SECOND: AtomicUsize = AtomicUsize::new(0); +static HWCAP: AtomicUsize = AtomicUsize::new(0); +static HWCAP2: AtomicUsize = AtomicUsize::new(0); +static MINSIGSTKSZ: AtomicUsize = AtomicUsize::new(0); +static SYSINFO_EHDR: AtomicPtr = AtomicPtr::new(null_mut()); +// Initialize `EXECFN` to a valid `CStr` pointer so that we don't need to check +// for null on every `execfn` call. +static EXECFN: AtomicPtr = AtomicPtr::new(b"\0".as_ptr() as _); +#[cfg(feature = "runtime")] +static SECURE: AtomicBool = AtomicBool::new(false); +// Use `dangling` so that we can always treat it like an empty slice. +#[cfg(feature = "runtime")] +static PHDR: AtomicPtr = AtomicPtr::new(NonNull::dangling().as_ptr()); +#[cfg(feature = "runtime")] +static PHENT: AtomicUsize = AtomicUsize::new(0); +#[cfg(feature = "runtime")] +static PHNUM: AtomicUsize = AtomicUsize::new(0); +#[cfg(feature = "runtime")] +static ENTRY: AtomicUsize = AtomicUsize::new(0); +#[cfg(feature = "runtime")] +static RANDOM: AtomicPtr<[u8; 16]> = AtomicPtr::new(NonNull::dangling().as_ptr()); + +/// When "use-explicitly-provided-auxv" is enabled, we export a function to be +/// called during initialization, and passed a pointer to the original +/// environment variable block set up by the OS. +pub(crate) unsafe fn init(envp: *mut *mut u8) { + init_from_envp(envp); +} + +/// # Safety +/// +/// This must be passed a pointer to the environment variable buffer +/// provided by the kernel, which is followed in memory by the auxv array. +unsafe fn init_from_envp(mut envp: *mut *mut u8) { + while !(*envp).is_null() { + envp = envp.add(1); + } + init_from_auxp(envp.add(1).cast()) +} + +/// Process auxv entries from the auxv array pointed to by `auxp`. +/// +/// # Safety +/// +/// This must be passed a pointer to an auxv array. +/// +/// The buffer contains `Elf_aux_t` elements, though it need not be aligned; +/// function uses `read_unaligned` to read from it. +unsafe fn init_from_auxp(mut auxp: *const Elf_auxv_t) { + loop { + let Elf_auxv_t { a_type, a_val } = read(auxp); + + match a_type as _ { + AT_PAGESZ => PAGE_SIZE.store(a_val as usize, Ordering::Relaxed), + AT_CLKTCK => CLOCK_TICKS_PER_SECOND.store(a_val as usize, Ordering::Relaxed), + AT_HWCAP => HWCAP.store(a_val as usize, Ordering::Relaxed), + AT_HWCAP2 => HWCAP2.store(a_val as usize, Ordering::Relaxed), + AT_MINSIGSTKSZ => MINSIGSTKSZ.store(a_val as usize, Ordering::Relaxed), + AT_EXECFN => EXECFN.store(a_val.cast::(), Ordering::Relaxed), + AT_SYSINFO_EHDR => SYSINFO_EHDR.store(a_val.cast::(), Ordering::Relaxed), + + #[cfg(feature = "runtime")] + AT_SECURE => SECURE.store(a_val as usize != 0, Ordering::Relaxed), + #[cfg(feature = "runtime")] + AT_PHDR => PHDR.store(a_val.cast::(), Ordering::Relaxed), + #[cfg(feature = "runtime")] + AT_PHNUM => PHNUM.store(a_val as usize, Ordering::Relaxed), + #[cfg(feature = "runtime")] + AT_PHENT => PHENT.store(a_val as usize, Ordering::Relaxed), + #[cfg(feature = "runtime")] + AT_ENTRY => ENTRY.store(a_val as usize, Ordering::Relaxed), + #[cfg(feature = "runtime")] + AT_RANDOM => RANDOM.store(a_val.cast::<[u8; 16]>(), Ordering::Relaxed), + + AT_NULL => break, + _ => (), + } + auxp = auxp.add(1); + } +} diff --git a/vendor/rustix/src/backend/linux_raw/param/libc_auxv.rs b/vendor/rustix/src/backend/linux_raw/param/libc_auxv.rs new file mode 100644 index 00000000..d77d3a84 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/param/libc_auxv.rs @@ -0,0 +1,198 @@ +//! Linux auxv support, using libc. +//! +//! # Safety +//! +//! This uses raw pointers to locate and read the kernel-provided auxv array. +#![allow(unsafe_code)] + +use crate::backend::c; +#[cfg(feature = "param")] +use crate::ffi::CStr; +#[cfg(not(feature = "runtime"))] +use core::ptr::null; +use linux_raw_sys::elf::*; + +// `getauxval` wasn't supported in glibc until 2.16. Also this lets us use +// `*mut` as the return type to preserve strict provenance. +#[cfg(not(feature = "runtime"))] +weak!(fn getauxval(c::c_ulong) -> *mut c::c_void); + +// With the "runtime" feature, go ahead and depend on `getauxval` existing so +// that we never fail. +#[cfg(feature = "runtime")] +extern "C" { + fn getauxval(type_: c::c_ulong) -> *mut c::c_void; +} + +#[cfg(feature = "runtime")] +const AT_PHDR: c::c_ulong = 3; +#[cfg(feature = "runtime")] +const AT_PHENT: c::c_ulong = 4; +#[cfg(feature = "runtime")] +const AT_PHNUM: c::c_ulong = 5; +#[cfg(feature = "runtime")] +const AT_ENTRY: c::c_ulong = 9; +const AT_HWCAP: c::c_ulong = 16; +#[cfg(feature = "runtime")] +const AT_RANDOM: c::c_ulong = 25; +const AT_HWCAP2: c::c_ulong = 26; +const AT_SECURE: c::c_ulong = 23; +const AT_EXECFN: c::c_ulong = 31; +const AT_SYSINFO_EHDR: c::c_ulong = 33; +const AT_MINSIGSTKSZ: c::c_ulong = 51; + +// Declare `sysconf` ourselves so that we don't depend on all of libc just for +// this. +extern "C" { + fn sysconf(name: c::c_int) -> c::c_long; +} + +#[cfg(target_os = "android")] +const _SC_PAGESIZE: c::c_int = 39; +#[cfg(target_os = "linux")] +const _SC_PAGESIZE: c::c_int = 30; +#[cfg(target_os = "android")] +const _SC_CLK_TCK: c::c_int = 6; +#[cfg(target_os = "linux")] +const _SC_CLK_TCK: c::c_int = 2; + +#[cfg(feature = "param")] +#[inline] +pub(crate) fn page_size() -> usize { + unsafe { sysconf(_SC_PAGESIZE) as usize } +} + +#[cfg(feature = "param")] +#[inline] +pub(crate) fn clock_ticks_per_second() -> u64 { + unsafe { sysconf(_SC_CLK_TCK) as u64 } +} + +#[cfg(feature = "param")] +#[inline] +pub(crate) fn linux_hwcap() -> (usize, usize) { + #[cfg(not(feature = "runtime"))] + unsafe { + if let Some(libc_getauxval) = getauxval.get() { + let hwcap = libc_getauxval(AT_HWCAP) as usize; + let hwcap2 = libc_getauxval(AT_HWCAP2) as usize; + (hwcap, hwcap2) + } else { + (0, 0) + } + } + + #[cfg(feature = "runtime")] + unsafe { + let hwcap = getauxval(AT_HWCAP) as usize; + let hwcap2 = getauxval(AT_HWCAP2) as usize; + (hwcap, hwcap2) + } +} + +#[cfg(feature = "param")] +#[inline] +pub(crate) fn linux_minsigstksz() -> usize { + #[cfg(not(feature = "runtime"))] + if let Some(libc_getauxval) = getauxval.get() { + unsafe { libc_getauxval(AT_MINSIGSTKSZ) as usize } + } else { + 0 + } + + #[cfg(feature = "runtime")] + unsafe { + getauxval(AT_MINSIGSTKSZ) as usize + } +} + +#[cfg(feature = "param")] +#[inline] +pub(crate) fn linux_execfn() -> &'static CStr { + #[cfg(not(feature = "runtime"))] + unsafe { + if let Some(libc_getauxval) = getauxval.get() { + CStr::from_ptr(libc_getauxval(AT_EXECFN).cast()) + } else { + cstr!("") + } + } + + #[cfg(feature = "runtime")] + unsafe { + CStr::from_ptr(getauxval(AT_EXECFN).cast()) + } +} + +#[cfg(feature = "runtime")] +#[inline] +pub(crate) fn linux_secure() -> bool { + unsafe { getauxval(AT_SECURE) as usize != 0 } +} + +#[cfg(feature = "runtime")] +#[inline] +pub(crate) fn exe_phdrs() -> (*const c::c_void, usize, usize) { + unsafe { + let phdr: *const c::c_void = getauxval(AT_PHDR); + let phent = getauxval(AT_PHENT) as usize; + let phnum = getauxval(AT_PHNUM) as usize; + (phdr, phent, phnum) + } +} + +/// `AT_SYSINFO_EHDR` isn't present on all platforms in all configurations, so +/// if we don't see it, this function returns a null pointer. +#[inline] +pub(in super::super) fn sysinfo_ehdr() -> *const Elf_Ehdr { + #[cfg(not(feature = "runtime"))] + unsafe { + if let Some(libc_getauxval) = getauxval.get() { + libc_getauxval(AT_SYSINFO_EHDR) as *const Elf_Ehdr + } else { + null() + } + } + + #[cfg(feature = "runtime")] + unsafe { + getauxval(AT_SYSINFO_EHDR) as *const Elf_Ehdr + } +} + +#[cfg(feature = "runtime")] +#[inline] +pub(crate) fn entry() -> usize { + unsafe { getauxval(AT_ENTRY) as usize } +} + +#[cfg(feature = "runtime")] +#[inline] +pub(crate) fn random() -> *const [u8; 16] { + unsafe { getauxval(AT_RANDOM) as *const [u8; 16] } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_abi() { + const_assert_eq!(self::_SC_PAGESIZE, ::libc::_SC_PAGESIZE); + const_assert_eq!(self::_SC_CLK_TCK, ::libc::_SC_CLK_TCK); + const_assert_eq!(self::AT_HWCAP, ::libc::AT_HWCAP); + const_assert_eq!(self::AT_HWCAP2, ::libc::AT_HWCAP2); + const_assert_eq!(self::AT_EXECFN, ::libc::AT_EXECFN); + const_assert_eq!(self::AT_SECURE, ::libc::AT_SECURE); + const_assert_eq!(self::AT_SYSINFO_EHDR, ::libc::AT_SYSINFO_EHDR); + const_assert_eq!(self::AT_MINSIGSTKSZ, ::libc::AT_MINSIGSTKSZ); + #[cfg(feature = "runtime")] + const_assert_eq!(self::AT_PHDR, ::libc::AT_PHDR); + #[cfg(feature = "runtime")] + const_assert_eq!(self::AT_PHNUM, ::libc::AT_PHNUM); + #[cfg(feature = "runtime")] + const_assert_eq!(self::AT_ENTRY, ::libc::AT_ENTRY); + #[cfg(feature = "runtime")] + const_assert_eq!(self::AT_RANDOM, ::libc::AT_RANDOM); + } +} diff --git a/vendor/rustix/src/backend/linux_raw/param/mod.rs b/vendor/rustix/src/backend/linux_raw/param/mod.rs new file mode 100644 index 00000000..365f0160 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/param/mod.rs @@ -0,0 +1,15 @@ +// With "use-explicitly-provided-auxv" enabled, we expect to be initialized +// with an explicit `rustix::param::init` call. +// +// With "use-libc-auxv" enabled, use libc's `getauxval`. +// +// Otherwise, we read aux values from /proc/self/auxv. +#[cfg_attr(feature = "use-explicitly-provided-auxv", path = "init.rs")] +#[cfg_attr( + all( + not(feature = "use-explicitly-provided-auxv"), + feature = "use-libc-auxv" + ), + path = "libc_auxv.rs" +)] +pub(crate) mod auxv; diff --git a/vendor/rustix/src/backend/linux_raw/pid/mod.rs b/vendor/rustix/src/backend/linux_raw/pid/mod.rs new file mode 100644 index 00000000..ef944f04 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/pid/mod.rs @@ -0,0 +1 @@ +pub(crate) mod syscalls; diff --git a/vendor/rustix/src/backend/linux_raw/pid/syscalls.rs b/vendor/rustix/src/backend/linux_raw/pid/syscalls.rs new file mode 100644 index 00000000..49aaf43e --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/pid/syscalls.rs @@ -0,0 +1,18 @@ +//! linux_raw syscalls for PIDs +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + +use crate::backend::conv::ret_usize_infallible; +use crate::pid::{Pid, RawPid}; + +#[inline] +#[must_use] +pub(crate) fn getpid() -> Pid { + unsafe { + let pid = ret_usize_infallible(syscall_readonly!(__NR_getpid)) as RawPid; + Pid::from_raw_unchecked(pid) + } +} diff --git a/vendor/rustix/src/backend/linux_raw/pipe/mod.rs b/vendor/rustix/src/backend/linux_raw/pipe/mod.rs new file mode 100644 index 00000000..1e0181a9 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/pipe/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/linux_raw/pipe/syscalls.rs b/vendor/rustix/src/backend/linux_raw/pipe/syscalls.rs new file mode 100644 index 00000000..86fe0855 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/pipe/syscalls.rs @@ -0,0 +1,135 @@ +//! linux_raw syscalls supporting `rustix::pipe`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + +use crate::backend::conv::{c_int, c_uint, opt_mut, pass_usize, ret, ret_usize, slice}; +use crate::backend::{c, MAX_IOV}; +use crate::fd::{BorrowedFd, OwnedFd}; +use crate::io; +use crate::pipe::{IoSliceRaw, PipeFlags, SpliceFlags}; +use core::cmp; +use core::mem::MaybeUninit; +use linux_raw_sys::general::{F_GETPIPE_SZ, F_SETPIPE_SZ}; + +#[inline] +pub(crate) fn pipe() -> io::Result<(OwnedFd, OwnedFd)> { + // aarch64 and risc64 omit `__NR_pipe`. On mips, `__NR_pipe` uses a special + // calling convention, but using it is not worth complicating our syscall + // wrapping infrastructure at this time. + #[cfg(any( + target_arch = "aarch64", + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "riscv64", + ))] + { + pipe_with(PipeFlags::empty()) + } + #[cfg(not(any( + target_arch = "aarch64", + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "riscv64", + )))] + unsafe { + let mut result = MaybeUninit::<[OwnedFd; 2]>::uninit(); + ret(syscall!(__NR_pipe, &mut result))?; + let [p0, p1] = result.assume_init(); + Ok((p0, p1)) + } +} + +#[inline] +pub(crate) fn pipe_with(flags: PipeFlags) -> io::Result<(OwnedFd, OwnedFd)> { + unsafe { + let mut result = MaybeUninit::<[OwnedFd; 2]>::uninit(); + ret(syscall!(__NR_pipe2, &mut result, flags))?; + let [p0, p1] = result.assume_init(); + Ok((p0, p1)) + } +} + +#[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 { + unsafe { + ret_usize(syscall!( + __NR_splice, + fd_in, + opt_mut(off_in), + fd_out, + opt_mut(off_out), + pass_usize(len), + flags + )) + } +} + +#[inline] +pub(crate) unsafe fn vmsplice( + fd: BorrowedFd<'_>, + bufs: &[IoSliceRaw<'_>], + flags: SpliceFlags, +) -> io::Result { + let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]); + ret_usize(syscall!(__NR_vmsplice, fd, bufs_addr, bufs_len, flags)) +} + +#[inline] +pub(crate) fn tee( + fd_in: BorrowedFd<'_>, + fd_out: BorrowedFd<'_>, + len: usize, + flags: SpliceFlags, +) -> io::Result { + unsafe { ret_usize(syscall!(__NR_tee, fd_in, fd_out, pass_usize(len), flags)) } +} + +#[inline] +pub(crate) fn fcntl_getpipe_size(fd: BorrowedFd<'_>) -> io::Result { + #[cfg(target_pointer_width = "32")] + unsafe { + ret_usize(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETPIPE_SZ))) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret_usize(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETPIPE_SZ))) + } +} + +#[inline] +pub(crate) fn fcntl_setpipe_size(fd: BorrowedFd<'_>, size: usize) -> io::Result { + let size: c::c_int = size.try_into().map_err(|_| io::Errno::PERM)?; + + #[cfg(target_pointer_width = "32")] + unsafe { + ret_usize(syscall_readonly!( + __NR_fcntl64, + fd, + c_uint(F_SETPIPE_SZ), + c_int(size) + )) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret_usize(syscall_readonly!( + __NR_fcntl, + fd, + c_uint(F_SETPIPE_SZ), + c_int(size) + )) + } +} diff --git a/vendor/rustix/src/backend/linux_raw/pipe/types.rs b/vendor/rustix/src/backend/linux_raw/pipe/types.rs new file mode 100644 index 00000000..ae185562 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/pipe/types.rs @@ -0,0 +1,85 @@ +use crate::backend::c; +use crate::ffi; +use bitflags::bitflags; +use core::marker::PhantomData; + +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: ffi::c_uint { + /// `O_CLOEXEC` + const CLOEXEC = linux_raw_sys::general::O_CLOEXEC; + /// `O_DIRECT` + const DIRECT = linux_raw_sys::general::O_DIRECT; + /// `O_NONBLOCK` + const NONBLOCK = linux_raw_sys::general::O_NONBLOCK; + + /// + const _ = !0; + } +} + +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 = linux_raw_sys::general::SPLICE_F_MOVE; + /// `SPLICE_F_NONBLOCK` + const NONBLOCK = linux_raw_sys::general::SPLICE_F_NONBLOCK; + /// `SPLICE_F_MORE` + const MORE = linux_raw_sys::general::SPLICE_F_MORE; + /// `SPLICE_F_GIFT` + const GIFT = linux_raw_sys::general::SPLICE_F_GIFT; + + /// + 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 +#[repr(transparent)] +pub struct IoSliceRaw<'a> { + _buf: c::iovec, + _lifetime: PhantomData<&'a ()>, +} + +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::(), + 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::(), + iov_len: buf.len() as _, + }, + _lifetime: PhantomData, + } + } +} diff --git a/vendor/rustix/src/backend/linux_raw/prctl/mod.rs b/vendor/rustix/src/backend/linux_raw/prctl/mod.rs new file mode 100644 index 00000000..ef944f04 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/prctl/mod.rs @@ -0,0 +1 @@ +pub(crate) mod syscalls; diff --git a/vendor/rustix/src/backend/linux_raw/prctl/syscalls.rs b/vendor/rustix/src/backend/linux_raw/prctl/syscalls.rs new file mode 100644 index 00000000..1410d512 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/prctl/syscalls.rs @@ -0,0 +1,21 @@ +//! linux_raw syscalls supporting modules that use `prctl`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + +use crate::backend::c; +use crate::backend::conv::{c_int, 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 { + ret_c_int(syscall!(__NR_prctl, c_int(option), arg2, arg3, arg4, arg5)) +} diff --git a/vendor/rustix/src/backend/linux_raw/process/mod.rs b/vendor/rustix/src/backend/linux_raw/process/mod.rs new file mode 100644 index 00000000..6bc9443d --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/process/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod syscalls; +pub(crate) mod types; +pub(crate) mod wait; diff --git a/vendor/rustix/src/backend/linux_raw/process/syscalls.rs b/vendor/rustix/src/backend/linux_raw/process/syscalls.rs new file mode 100644 index 00000000..c9c1fd82 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/process/syscalls.rs @@ -0,0 +1,560 @@ +//! linux_raw syscalls supporting `rustix::process`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + +use crate::backend::c; +#[cfg(all(feature = "alloc", feature = "fs"))] +use crate::backend::conv::slice_mut; +use crate::backend::conv::{ + by_mut, by_ref, c_int, c_uint, negative_pid, pass_usize, raw_fd, ret, ret_c_int, + ret_c_int_infallible, ret_infallible, ret_owned_fd, zero, +}; +use crate::fd::{AsRawFd as _, BorrowedFd, OwnedFd, RawFd}; +#[cfg(feature = "fs")] +use crate::ffi::CStr; +use crate::io; +use crate::pid::RawPid; +use crate::process::{ + Flock, Pid, PidfdFlags, PidfdGetfdFlags, Resource, Rlimit, Uid, WaitId, WaitIdOptions, + WaitIdStatus, WaitOptions, WaitStatus, +}; +use crate::signal::Signal; +use core::mem::MaybeUninit; +use core::ptr::{null, null_mut}; +use linux_raw_sys::general::{rlimit64, PRIO_PGRP, PRIO_PROCESS, PRIO_USER, RLIM64_INFINITY}; +#[cfg(feature = "fs")] +use {crate::backend::conv::ret_c_uint_infallible, crate::fs::Mode}; +#[cfg(feature = "alloc")] +use { + crate::backend::conv::{ret_usize, slice_just_addr_mut}, + crate::process::Gid, +}; + +#[cfg(feature = "fs")] +#[inline] +pub(crate) fn chdir(filename: &CStr) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_chdir, filename)) } +} + +#[inline] +pub(crate) fn fchdir(fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_fchdir, fd)) } +} + +#[cfg(feature = "fs")] +#[inline] +pub(crate) fn chroot(filename: &CStr) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_chroot, filename)) } +} + +#[cfg(all(feature = "alloc", feature = "fs"))] +#[inline] +pub(crate) fn getcwd(buf: &mut [MaybeUninit]) -> io::Result { + let (buf_addr_mut, buf_len) = slice_mut(buf); + unsafe { ret_usize(syscall!(__NR_getcwd, buf_addr_mut, buf_len)) } +} + +#[inline] +#[must_use] +pub(crate) fn getppid() -> Option { + unsafe { + let ppid = ret_c_int_infallible(syscall_readonly!(__NR_getppid)); + Pid::from_raw(ppid) + } +} + +#[inline] +pub(crate) fn getpgid(pid: Option) -> io::Result { + unsafe { + let pgid = ret_c_int(syscall_readonly!(__NR_getpgid, c_int(Pid::as_raw(pid))))?; + debug_assert!(pgid > 0); + Ok(Pid::from_raw_unchecked(pgid)) + } +} + +#[inline] +pub(crate) fn setpgid(pid: Option, pgid: Option) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_setpgid, + c_int(Pid::as_raw(pid)), + c_int(Pid::as_raw(pgid)) + )) + } +} + +#[inline] +#[must_use] +pub(crate) fn getpgrp() -> Pid { + // Use the `getpgrp` syscall if available. + #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] + unsafe { + let pgid = ret_c_int_infallible(syscall_readonly!(__NR_getpgrp)); + debug_assert!(pgid > 0); + Pid::from_raw_unchecked(pgid) + } + + // Otherwise use `getpgrp` and pass it zero. + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + unsafe { + let pgid = ret_c_int_infallible(syscall_readonly!(__NR_getpgid, c_uint(0))); + debug_assert!(pgid > 0); + Pid::from_raw_unchecked(pgid) + } +} + +#[cfg(feature = "fs")] +#[inline] +pub(crate) fn umask(mode: Mode) -> Mode { + unsafe { Mode::from_bits_retain(ret_c_uint_infallible(syscall_readonly!(__NR_umask, mode))) } +} + +#[inline] +pub(crate) fn nice(inc: i32) -> io::Result { + let priority = (if inc > -40 && inc < 40 { + inc + getpriority_process(None)? + } else { + inc + }) + .clamp(-20, 19); + setpriority_process(None, priority)?; + Ok(priority) +} + +#[inline] +pub(crate) fn getpriority_user(uid: Uid) -> io::Result { + unsafe { + Ok(20 + - ret_c_int(syscall_readonly!( + __NR_getpriority, + c_uint(PRIO_USER), + c_uint(uid.as_raw()) + ))?) + } +} + +#[inline] +pub(crate) fn getpriority_pgrp(pgid: Option) -> io::Result { + unsafe { + Ok(20 + - ret_c_int(syscall_readonly!( + __NR_getpriority, + c_uint(PRIO_PGRP), + c_int(Pid::as_raw(pgid)) + ))?) + } +} + +#[inline] +pub(crate) fn getpriority_process(pid: Option) -> io::Result { + unsafe { + Ok(20 + - ret_c_int(syscall_readonly!( + __NR_getpriority, + c_uint(PRIO_PROCESS), + c_int(Pid::as_raw(pid)) + ))?) + } +} + +#[inline] +pub(crate) fn setpriority_user(uid: Uid, priority: i32) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_setpriority, + c_uint(PRIO_USER), + c_uint(uid.as_raw()), + c_int(priority) + )) + } +} + +#[inline] +pub(crate) fn setpriority_pgrp(pgid: Option, priority: i32) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_setpriority, + c_uint(PRIO_PGRP), + c_int(Pid::as_raw(pgid)), + c_int(priority) + )) + } +} + +#[inline] +pub(crate) fn setpriority_process(pid: Option, priority: i32) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_setpriority, + c_uint(PRIO_PROCESS), + c_int(Pid::as_raw(pid)), + c_int(priority) + )) + } +} + +#[inline] +pub(crate) fn getrlimit(limit: Resource) -> Rlimit { + let mut result = MaybeUninit::::uninit(); + unsafe { + ret_infallible(syscall!( + __NR_prlimit64, + c_uint(0), + limit, + null::(), + &mut result + )); + rlimit_from_linux(result.assume_init()) + } +} + +#[inline] +pub(crate) fn setrlimit(limit: Resource, new: Rlimit) -> io::Result<()> { + unsafe { + let lim = rlimit_to_linux(new); + match ret(syscall_readonly!( + __NR_prlimit64, + c_uint(0), + limit, + by_ref(&lim), + null_mut::() + )) { + Ok(()) => Ok(()), + Err(err) => Err(err), + } + } +} + +#[inline] +pub(crate) fn prlimit(pid: Option, limit: Resource, new: Rlimit) -> io::Result { + let lim = rlimit_to_linux(new); + let mut result = MaybeUninit::::uninit(); + unsafe { + match ret(syscall!( + __NR_prlimit64, + c_int(Pid::as_raw(pid)), + limit, + by_ref(&lim), + &mut result + )) { + Ok(()) => Ok(rlimit_from_linux(result.assume_init())), + Err(err) => Err(err), + } + } +} + +/// Convert a C `rlimit64` to a Rust `Rlimit`. +#[inline] +fn rlimit_from_linux(lim: rlimit64) -> Rlimit { + let current = if lim.rlim_cur == RLIM64_INFINITY as u64 { + None + } else { + Some(lim.rlim_cur) + }; + let maximum = if lim.rlim_max == RLIM64_INFINITY as u64 { + None + } else { + Some(lim.rlim_max) + }; + Rlimit { current, maximum } +} + +/// Convert a Rust [`Rlimit`] to a C `rlimit64`. +#[inline] +fn rlimit_to_linux(lim: Rlimit) -> rlimit64 { + let rlim_cur = match lim.current { + Some(r) => r, + None => RLIM64_INFINITY as _, + }; + let rlim_max = match lim.maximum { + Some(r) => r, + None => RLIM64_INFINITY as _, + }; + rlimit64 { rlim_cur, rlim_max } +} + +#[inline] +pub(crate) fn wait(waitopts: WaitOptions) -> io::Result> { + _waitpid(!0, waitopts) +} + +#[inline] +pub(crate) fn waitpid( + pid: Option, + waitopts: WaitOptions, +) -> io::Result> { + _waitpid(Pid::as_raw(pid), waitopts) +} + +#[inline] +pub(crate) fn waitpgid(pgid: Pid, waitopts: WaitOptions) -> io::Result> { + _waitpid(-pgid.as_raw_nonzero().get(), waitopts) +} + +#[inline] +pub(crate) fn _waitpid( + pid: RawPid, + waitopts: WaitOptions, +) -> io::Result> { + unsafe { + let mut status = MaybeUninit::::uninit(); + let pid = ret_c_int(syscall!( + __NR_wait4, + c_int(pid as _), + &mut status, + c_int(waitopts.bits() as _), + zero() + ))?; + Ok(Pid::from_raw(pid).map(|pid| (pid, WaitStatus::new(status.assume_init())))) + } +} + +#[inline] +pub(crate) fn waitid(id: WaitId<'_>, options: WaitIdOptions) -> io::Result> { + // Get the id to wait on. + match id { + WaitId::All => _waitid_all(options), + WaitId::Pid(pid) => _waitid_pid(pid, options), + WaitId::Pgid(pid) => _waitid_pgid(pid, options), + WaitId::PidFd(fd) => _waitid_pidfd(fd, options), + } +} + +#[inline] +fn _waitid_all(options: WaitIdOptions) -> io::Result> { + // `waitid` can return successfully without initializing the struct (no + // children found when using `WNOHANG`) + let mut status = MaybeUninit::::zeroed(); + unsafe { + ret(syscall!( + __NR_waitid, + c_uint(c::P_ALL), + c_uint(0), + &mut status, + c_int(options.bits() as _), + zero() + ))? + }; + + Ok(unsafe { cvt_waitid_status(status) }) +} + +#[inline] +fn _waitid_pid(pid: Pid, options: WaitIdOptions) -> io::Result> { + // `waitid` can return successfully without initializing the struct (no + // children found when using `WNOHANG`) + let mut status = MaybeUninit::::zeroed(); + unsafe { + ret(syscall!( + __NR_waitid, + c_uint(c::P_PID), + c_int(Pid::as_raw(Some(pid))), + &mut status, + c_int(options.bits() as _), + zero() + ))? + }; + + Ok(unsafe { cvt_waitid_status(status) }) +} + +#[inline] +fn _waitid_pgid(pgid: Option, options: WaitIdOptions) -> io::Result> { + // `waitid` can return successfully without initializing the struct (no + // children found when using `WNOHANG`) + let mut status = MaybeUninit::::zeroed(); + unsafe { + ret(syscall!( + __NR_waitid, + c_uint(c::P_PGID), + c_int(Pid::as_raw(pgid)), + &mut status, + c_int(options.bits() as _), + zero() + ))? + }; + + Ok(unsafe { cvt_waitid_status(status) }) +} + +#[inline] +fn _waitid_pidfd(fd: BorrowedFd<'_>, options: WaitIdOptions) -> io::Result> { + // `waitid` can return successfully without initializing the struct (no + // children found when using `WNOHANG`) + let mut status = MaybeUninit::::zeroed(); + unsafe { + ret(syscall!( + __NR_waitid, + c_uint(c::P_PIDFD), + c_uint(fd.as_raw_fd() as _), + &mut status, + c_int(options.bits() as _), + zero() + ))? + }; + + 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. +#[inline] +unsafe fn cvt_waitid_status(status: MaybeUninit) -> Option { + let status = status.assume_init(); + if status + .__bindgen_anon_1 + .__bindgen_anon_1 + ._sifields + ._sigchld + ._pid + == 0 + { + None + } else { + Some(WaitIdStatus(status)) + } +} + +#[inline] +pub(crate) fn getsid(pid: Option) -> io::Result { + unsafe { + let pid = ret_c_int(syscall_readonly!(__NR_getsid, c_int(Pid::as_raw(pid))))?; + Ok(Pid::from_raw_unchecked(pid)) + } +} + +#[inline] +pub(crate) fn setsid() -> io::Result { + unsafe { + let pid = ret_c_int(syscall_readonly!(__NR_setsid))?; + Ok(Pid::from_raw_unchecked(pid)) + } +} + +#[inline] +pub(crate) fn kill_process(pid: Pid, sig: Signal) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_kill, pid, sig)) } +} + +#[inline] +pub(crate) fn kill_process_group(pid: Pid, sig: Signal) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_kill, negative_pid(pid), sig)) } +} + +#[inline] +pub(crate) fn kill_current_process_group(sig: Signal) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_kill, pass_usize(0), sig)) } +} + +#[inline] +pub(crate) fn test_kill_process(pid: Pid) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_kill, pid, pass_usize(0))) } +} + +#[inline] +pub(crate) fn test_kill_process_group(pid: Pid) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_kill, + negative_pid(pid), + pass_usize(0) + )) + } +} + +#[inline] +pub(crate) fn test_kill_current_process_group() -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_kill, pass_usize(0), pass_usize(0))) } +} + +#[inline] +pub(crate) fn pidfd_getfd( + pidfd: BorrowedFd<'_>, + targetfd: RawFd, + flags: PidfdGetfdFlags, +) -> io::Result { + unsafe { + ret_owned_fd(syscall_readonly!( + __NR_pidfd_getfd, + pidfd, + raw_fd(targetfd), + c_int(flags.bits() as _) + )) + } +} + +#[inline] +pub(crate) fn pidfd_open(pid: Pid, flags: PidfdFlags) -> io::Result { + unsafe { ret_owned_fd(syscall_readonly!(__NR_pidfd_open, pid, flags)) } +} + +#[inline] +pub(crate) fn pidfd_send_signal(fd: BorrowedFd<'_>, sig: Signal) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_pidfd_send_signal, + fd, + sig, + pass_usize(0), + pass_usize(0) + )) + } +} + +#[cfg(feature = "fs")] +#[inline] +pub(crate) fn pivot_root(new_root: &CStr, put_old: &CStr) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_pivot_root, new_root, put_old)) } +} + +#[cfg(feature = "alloc")] +#[inline] +pub(crate) fn getgroups(buf: &mut [Gid]) -> io::Result { + let len = buf.len().try_into().map_err(|_| io::Errno::NOMEM)?; + + unsafe { + ret_usize(syscall!( + __NR_getgroups, + c_int(len), + slice_just_addr_mut(buf) + )) + } +} + +#[inline] +pub(crate) fn fcntl_getlk(fd: BorrowedFd<'_>, lock: &Flock) -> io::Result> { + let mut curr_lock: c::flock = lock.as_raw(); + #[cfg(target_pointer_width = "32")] + unsafe { + ret(syscall!( + __NR_fcntl64, + fd, + c_uint(c::F_GETLK64), + by_mut(&mut curr_lock) + ))? + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret(syscall!( + __NR_fcntl, + fd, + c_uint(c::F_GETLK), + by_mut(&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) })) + } +} diff --git a/vendor/rustix/src/backend/linux_raw/process/types.rs b/vendor/rustix/src/backend/linux_raw/process/types.rs new file mode 100644 index 00000000..58b8cc14 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/process/types.rs @@ -0,0 +1,43 @@ +/// A resource value for use with [`getrlimit`], [`setrlimit`], and +/// [`prlimit`]. +/// +/// [`getrlimit`]: crate::process::getrlimit +/// [`setrlimit`]: crate::process::setrlimit +/// [`prlimit`]: crate::process::prlimit +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[repr(u32)] +#[non_exhaustive] +pub enum Resource { + /// `RLIMIT_CPU` + Cpu = linux_raw_sys::general::RLIMIT_CPU, + /// `RLIMIT_FSIZE` + Fsize = linux_raw_sys::general::RLIMIT_FSIZE, + /// `RLIMIT_DATA` + Data = linux_raw_sys::general::RLIMIT_DATA, + /// `RLIMIT_STACK` + Stack = linux_raw_sys::general::RLIMIT_STACK, + /// `RLIMIT_CORE` + Core = linux_raw_sys::general::RLIMIT_CORE, + /// `RLIMIT_RSS` + Rss = linux_raw_sys::general::RLIMIT_RSS, + /// `RLIMIT_NPROC` + Nproc = linux_raw_sys::general::RLIMIT_NPROC, + /// `RLIMIT_NOFILE` + Nofile = linux_raw_sys::general::RLIMIT_NOFILE, + /// `RLIMIT_MEMLOCK` + Memlock = linux_raw_sys::general::RLIMIT_MEMLOCK, + /// `RLIMIT_AS` + As = linux_raw_sys::general::RLIMIT_AS, + /// `RLIMIT_LOCKS` + Locks = linux_raw_sys::general::RLIMIT_LOCKS, + /// `RLIMIT_SIGPENDING` + Sigpending = linux_raw_sys::general::RLIMIT_SIGPENDING, + /// `RLIMIT_MSGQUEUE` + Msgqueue = linux_raw_sys::general::RLIMIT_MSGQUEUE, + /// `RLIMIT_NICE` + Nice = linux_raw_sys::general::RLIMIT_NICE, + /// `RLIMIT_RTPRIO` + Rtprio = linux_raw_sys::general::RLIMIT_RTPRIO, + /// `RLIMIT_RTTIME` + Rttime = linux_raw_sys::general::RLIMIT_RTTIME, +} diff --git a/vendor/rustix/src/backend/linux_raw/process/wait.rs b/vendor/rustix/src/backend/linux_raw/process/wait.rs new file mode 100644 index 00000000..89b3eadb --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/process/wait.rs @@ -0,0 +1,123 @@ +// The functions replacing the C macros use the same names as in libc. +#![allow(non_snake_case, unsafe_code)] + +use crate::ffi::c_int; +pub(crate) use linux_raw_sys::general::{ + siginfo_t, WCONTINUED, WEXITED, WNOHANG, WNOWAIT, WSTOPPED, WUNTRACED, +}; + +#[inline] +pub(crate) fn WIFSTOPPED(status: i32) -> bool { + (status & 0xff) == 0x7f +} + +#[inline] +pub(crate) fn WSTOPSIG(status: i32) -> i32 { + (status >> 8) & 0xff +} + +#[inline] +pub(crate) fn WIFCONTINUED(status: i32) -> bool { + status == 0xffff +} + +#[inline] +pub(crate) fn WIFSIGNALED(status: i32) -> bool { + ((status & 0x7f) + 1) as i8 >= 2 +} + +#[inline] +pub(crate) fn WTERMSIG(status: i32) -> i32 { + status & 0x7f +} + +#[inline] +pub(crate) fn WIFEXITED(status: i32) -> bool { + (status & 0x7f) == 0 +} + +#[inline] +pub(crate) fn WEXITSTATUS(status: i32) -> i32 { + (status >> 8) & 0xff +} + +pub(crate) trait SiginfoExt { + fn si_signo(&self) -> c_int; + fn si_errno(&self) -> c_int; + fn si_code(&self) -> c_int; + unsafe fn si_status(&self) -> c_int; +} + +impl SiginfoExt for siginfo_t { + #[inline] + fn si_signo(&self) -> c_int { + // SAFETY: This is technically a union access, but it's only a union + // with padding. + unsafe { self.__bindgen_anon_1.__bindgen_anon_1.si_signo } + } + + #[inline] + fn si_errno(&self) -> c_int { + // SAFETY: This is technically a union access, but it's only a union + // with padding. + unsafe { self.__bindgen_anon_1.__bindgen_anon_1.si_errno } + } + + #[inline] + fn si_code(&self) -> c_int { + // SAFETY: This is technically a union access, but it's only a union + // with padding. + unsafe { self.__bindgen_anon_1.__bindgen_anon_1.si_code } + } + + /// Return the exit status or signal number recorded in a `siginfo_t`. + /// + /// # Safety + /// + /// `si_signo` must equal `SIGCHLD` (as it is guaranteed to do after a + /// `waitid` call). + #[inline] + unsafe fn si_status(&self) -> c_int { + self.__bindgen_anon_1 + .__bindgen_anon_1 + ._sifields + ._sigchld + ._status + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_libc_correspondence() { + for status in [ + 0, + 1, + 63, + 64, + 65, + 127, + 128, + 129, + 255, + 256, + 257, + 4095, + 4096, + 4097, + i32::MAX, + i32::MIN, + u32::MAX as i32, + ] { + assert_eq!(WIFSTOPPED(status), libc::WIFSTOPPED(status)); + assert_eq!(WSTOPSIG(status), libc::WSTOPSIG(status)); + assert_eq!(WIFCONTINUED(status), libc::WIFCONTINUED(status)); + assert_eq!(WIFSIGNALED(status), libc::WIFSIGNALED(status)); + assert_eq!(WTERMSIG(status), libc::WTERMSIG(status)); + assert_eq!(WIFEXITED(status), libc::WIFEXITED(status)); + assert_eq!(WEXITSTATUS(status), libc::WEXITSTATUS(status)); + } + } +} diff --git a/vendor/rustix/src/backend/linux_raw/pty/mod.rs b/vendor/rustix/src/backend/linux_raw/pty/mod.rs new file mode 100644 index 00000000..ef944f04 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/pty/mod.rs @@ -0,0 +1 @@ +pub(crate) mod syscalls; diff --git a/vendor/rustix/src/backend/linux_raw/pty/syscalls.rs b/vendor/rustix/src/backend/linux_raw/pty/syscalls.rs new file mode 100644 index 00000000..b64344fb --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/pty/syscalls.rs @@ -0,0 +1,43 @@ +//! linux_raw syscalls supporting `rustix::pty`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + +use crate::backend::conv::{by_ref, c_uint, ret}; +use crate::fd::BorrowedFd; +use crate::io; +use linux_raw_sys::ioctl::TIOCSPTLCK; +#[cfg(feature = "alloc")] +use { + crate::backend::c, crate::ffi::CString, crate::path::DecInt, alloc::vec::Vec, + core::mem::MaybeUninit, linux_raw_sys::ioctl::TIOCGPTN, +}; + +#[cfg(feature = "alloc")] +#[inline] +pub(crate) fn ptsname(fd: BorrowedFd<'_>, mut buffer: Vec) -> io::Result { + unsafe { + let mut n = MaybeUninit::::uninit(); + ret(syscall!(__NR_ioctl, fd, c_uint(TIOCGPTN), &mut n))?; + + buffer.clear(); + buffer.extend_from_slice(b"/dev/pts/"); + buffer.extend_from_slice(DecInt::new(n.assume_init()).as_bytes()); + buffer.push(b'\0'); + Ok(CString::from_vec_with_nul_unchecked(buffer)) + } +} + +#[inline] +pub(crate) fn unlockpt(fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_ioctl, + fd, + c_uint(TIOCSPTLCK), + by_ref(&0) + )) + } +} diff --git a/vendor/rustix/src/backend/linux_raw/rand/mod.rs b/vendor/rustix/src/backend/linux_raw/rand/mod.rs new file mode 100644 index 00000000..1e0181a9 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/rand/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/linux_raw/rand/syscalls.rs b/vendor/rustix/src/backend/linux_raw/rand/syscalls.rs new file mode 100644 index 00000000..acea3968 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/rand/syscalls.rs @@ -0,0 +1,15 @@ +//! linux_raw syscalls supporting `rustix::rand`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + +use crate::backend::conv::{pass_usize, ret_usize}; +use crate::io; +use crate::rand::GetRandomFlags; + +#[inline] +pub(crate) unsafe fn getrandom(buf: (*mut u8, usize), flags: GetRandomFlags) -> io::Result { + ret_usize(syscall!(__NR_getrandom, buf.0, pass_usize(buf.1), flags)) +} diff --git a/vendor/rustix/src/backend/linux_raw/rand/types.rs b/vendor/rustix/src/backend/linux_raw/rand/types.rs new file mode 100644 index 00000000..9bc857fd --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/rand/types.rs @@ -0,0 +1,20 @@ +use bitflags::bitflags; + +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 = linux_raw_sys::general::GRND_RANDOM; + /// `GRND_NONBLOCK` + const NONBLOCK = linux_raw_sys::general::GRND_NONBLOCK; + /// `GRND_INSECURE` + const INSECURE = linux_raw_sys::general::GRND_INSECURE; + + /// + const _ = !0; + } +} diff --git a/vendor/rustix/src/backend/linux_raw/reg.rs b/vendor/rustix/src/backend/linux_raw/reg.rs new file mode 100644 index 00000000..57a8d269 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/reg.rs @@ -0,0 +1,259 @@ +//! Encapsulation for system call arguments and return values. +//! +//! The inline-asm code paths do some amount of reordering of arguments; to +//! ensure that we don't accidentally misroute an argument or return value, we +//! use distinct types for each argument index and return value. +//! +//! # Safety +//! +//! The `ToAsm` and `FromAsm` traits are unsafe to use; they should only be +//! used by the syscall code which executes actual syscall machine +//! instructions. + +#![allow(unsafe_code)] + +use super::c; +use super::fd::RawFd; +use core::marker::PhantomData; +use core::ops::Range; + +pub(super) trait ToAsm: private::Sealed { + /// Convert `self` to a `usize` ready to be passed to a syscall + /// machine instruction. + /// + /// # Safety + /// + /// This should be used immediately before the syscall instruction, and the + /// returned value shouldn't be used for any other purpose. + #[must_use] + unsafe fn to_asm(self) -> *mut Opaque; +} + +pub(super) trait FromAsm: private::Sealed { + /// Convert `raw` from a value produced by a syscall machine instruction + /// into a `Self`. + /// + /// # Safety + /// + /// This should be used immediately after the syscall instruction, and the + /// operand value shouldn't be used for any other purpose. + #[must_use] + unsafe fn from_asm(raw: *mut Opaque) -> Self; +} + +/// To preserve provenance, syscall arguments and return values are passed as +/// pointer types. They need a type to point to, so we define a custom private +/// type, to prevent it from being used for anything else. +#[repr(transparent)] +#[allow(dead_code)] +pub(super) struct Opaque(c::c_void); + +// Argument numbers. +pub(super) struct A0(()); +pub(super) struct A1(()); +pub(super) struct A2(()); +pub(super) struct A3(()); +pub(super) struct A4(()); +pub(super) struct A5(()); +#[cfg(any(target_arch = "mips", target_arch = "mips32r6"))] +pub(super) struct A6(()); +#[cfg(target_arch = "x86")] +pub(super) struct SocketArg; + +pub(super) trait ArgNumber: private::Sealed {} +impl ArgNumber for A0 {} +impl ArgNumber for A1 {} +impl ArgNumber for A2 {} +impl ArgNumber for A3 {} +impl ArgNumber for A4 {} +impl ArgNumber for A5 {} +#[cfg(any(target_arch = "mips", target_arch = "mips32r6"))] +impl ArgNumber for A6 {} +#[cfg(target_arch = "x86")] +impl ArgNumber for SocketArg {} + +// Return value numbers. +pub(super) struct R0(()); + +pub(super) trait RetNumber: private::Sealed {} +impl RetNumber for R0 {} + +/// Syscall arguments use register-sized types. We use a newtype to +/// discourage accidental misuse of the raw integer values. +/// +/// This type doesn't implement `Clone` or `Copy`; it should be used exactly +/// once. And it has a lifetime to ensure that it doesn't outlive any resources +/// it might be pointing to. +#[repr(transparent)] +#[must_use] +pub(super) struct ArgReg<'a, Num: ArgNumber> { + raw: *mut Opaque, + _phantom: PhantomData<(&'a (), Num)>, +} + +impl<'a, Num: ArgNumber> ToAsm for ArgReg<'a, Num> { + #[inline] + unsafe fn to_asm(self) -> *mut Opaque { + self.raw + } +} + +/// Syscall return values use register-sized types. We use a newtype to +/// discourage accidental misuse of the raw integer values. +/// +/// This type doesn't implement `Clone` or `Copy`; it should be used exactly +/// once. +#[repr(transparent)] +#[must_use] +pub(super) struct RetReg { + raw: *mut Opaque, + _phantom: PhantomData, +} + +impl RetReg { + #[inline] + pub(super) fn decode_usize(self) -> usize { + debug_assert!(!(-4095..0).contains(&(self.raw as isize))); + self.raw as usize + } + + #[inline] + pub(super) fn decode_raw_fd(self) -> RawFd { + let bits = self.decode_usize(); + let raw_fd = bits as RawFd; + + // Converting `raw` to `RawFd` should be lossless. + debug_assert_eq!(raw_fd as usize, bits); + + raw_fd + } + + #[inline] + pub(super) fn decode_c_int(self) -> c::c_int { + let bits = self.decode_usize(); + let c_int_ = bits as c::c_int; + + // Converting `raw` to `c_int` should be lossless. + debug_assert_eq!(c_int_ as usize, bits); + + c_int_ + } + + #[inline] + pub(super) fn decode_c_uint(self) -> c::c_uint { + let bits = self.decode_usize(); + let c_uint_ = bits as c::c_uint; + + // Converting `raw` to `c_uint` should be lossless. + debug_assert_eq!(c_uint_ as usize, bits); + + c_uint_ + } + + #[inline] + pub(super) fn decode_void_star(self) -> *mut c::c_void { + self.raw.cast() + } + + #[cfg(target_pointer_width = "64")] + #[inline] + pub(super) fn decode_u64(self) -> u64 { + self.decode_usize() as u64 + } + + #[inline] + pub(super) fn decode_void(self) { + let ignore = self.decode_usize(); + debug_assert_eq!(ignore, 0); + } + + #[inline] + pub(super) fn decode_error_code(self) -> u16 { + let bits = self.raw as usize; + + // `raw` must be in `-4095..0`. Linux always returns errors in + // `-4095..0`, and we double-check it here. + debug_assert!((-4095..0).contains(&(bits as isize))); + + bits as u16 + } + + #[inline] + pub(super) fn is_nonzero(&self) -> bool { + !self.raw.is_null() + } + + #[inline] + pub(super) fn is_negative(&self) -> bool { + (self.raw as isize) < 0 + } + + #[inline] + pub(super) fn is_in_range(&self, range: Range) -> bool { + range.contains(&(self.raw as isize)) + } +} + +impl FromAsm for RetReg { + #[inline] + unsafe fn from_asm(raw: *mut Opaque) -> Self { + Self { + raw, + _phantom: PhantomData, + } + } +} + +#[repr(transparent)] +pub(super) struct SyscallNumber<'a> { + pub(super) nr: usize, + _phantom: PhantomData<&'a ()>, +} + +impl<'a> ToAsm for SyscallNumber<'a> { + #[inline] + unsafe fn to_asm(self) -> *mut Opaque { + self.nr as usize as *mut Opaque + } +} + +/// Encode a system call argument as an `ArgReg`. +#[inline] +pub(super) fn raw_arg<'a, Num: ArgNumber>(raw: *mut Opaque) -> ArgReg<'a, Num> { + ArgReg { + raw, + _phantom: PhantomData, + } +} + +/// Encode a system call number (a `__NR_*` constant) as a `SyscallNumber`. +#[inline] +pub(super) const fn nr<'a>(nr: u32) -> SyscallNumber<'a> { + SyscallNumber { + nr: nr as usize, + _phantom: PhantomData, + } +} + +/// Seal our various traits using the technique documented [here]. +/// +/// [here]: https://rust-lang.github.io/api-guidelines/future-proofing.html +mod private { + pub trait Sealed {} + + // Implement for those same types, but no others. + impl<'a, Num: super::ArgNumber> Sealed for super::ArgReg<'a, Num> {} + impl Sealed for super::RetReg {} + impl<'a> Sealed for super::SyscallNumber<'a> {} + impl Sealed for super::A0 {} + impl Sealed for super::A1 {} + impl Sealed for super::A2 {} + impl Sealed for super::A3 {} + impl Sealed for super::A4 {} + impl Sealed for super::A5 {} + #[cfg(any(target_arch = "mips", target_arch = "mips32r6"))] + impl Sealed for super::A6 {} + #[cfg(target_arch = "x86")] + impl Sealed for super::SocketArg {} + impl Sealed for super::R0 {} +} diff --git a/vendor/rustix/src/backend/linux_raw/runtime/mod.rs b/vendor/rustix/src/backend/linux_raw/runtime/mod.rs new file mode 100644 index 00000000..0b48649c --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/runtime/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod syscalls; +pub(crate) mod tls; diff --git a/vendor/rustix/src/backend/linux_raw/runtime/syscalls.rs b/vendor/rustix/src/backend/linux_raw/runtime/syscalls.rs new file mode 100644 index 00000000..1e00030c --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/runtime/syscalls.rs @@ -0,0 +1,346 @@ +//! linux_raw syscalls supporting `rustix::runtime`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + +use crate::backend::c; +#[cfg(target_arch = "x86")] +use crate::backend::conv::by_mut; +#[cfg(target_arch = "x86_64")] +use crate::backend::conv::c_uint; +use crate::backend::conv::{ + by_ref, c_int, opt_ref, ret, ret_c_int, ret_c_int_infallible, ret_error, ret_infallible, + ret_void_star, size_of, zero, +}; +#[cfg(feature = "fs")] +use crate::fd::BorrowedFd; +use crate::ffi::CStr; +#[cfg(feature = "fs")] +use crate::fs::AtFlags; +use crate::io; +use crate::pid::{Pid, RawPid}; +use crate::runtime::{Fork, How, KernelSigSet, KernelSigaction, Siginfo, Stack}; +use crate::signal::Signal; +use crate::timespec::Timespec; +use core::ffi::c_void; +use core::mem::MaybeUninit; +#[cfg(all(target_pointer_width = "32", not(feature = "linux_5_1")))] +use linux_raw_sys::general::__kernel_old_timespec; +#[cfg(target_arch = "x86_64")] +use linux_raw_sys::general::ARCH_SET_FS; + +#[inline] +pub(crate) unsafe fn kernel_fork() -> io::Result { + let mut child_pid = MaybeUninit::::uninit(); + + // Unix `fork` only returns the child PID in the parent; we'd like it in + // the child too, so set `CLONE_CHILD_SETTID` and pass in the address of a + // memory location to store it to in the child. + // + // Architectures differ on the order of the parameters. + #[cfg(target_arch = "x86_64")] + let pid = ret_c_int(syscall!( + __NR_clone, + c_int(c::SIGCHLD | c::CLONE_CHILD_SETTID), + zero(), + zero(), + &mut child_pid, + zero() + ))?; + #[cfg(any( + target_arch = "aarch64", + target_arch = "arm", + 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" + ))] + let pid = ret_c_int(syscall!( + __NR_clone, + c_int(c::SIGCHLD | c::CLONE_CHILD_SETTID), + zero(), + zero(), + zero(), + &mut child_pid + ))?; + + Ok(if let Some(pid) = Pid::from_raw(pid) { + Fork::ParentOf(pid) + } else { + Fork::Child(Pid::from_raw_unchecked(child_pid.assume_init())) + }) +} + +#[cfg(feature = "fs")] +pub(crate) unsafe fn execveat( + dirfd: BorrowedFd<'_>, + path: &CStr, + args: *const *const u8, + env_vars: *const *const u8, + flags: AtFlags, +) -> io::Errno { + ret_error(syscall_readonly!( + __NR_execveat, + dirfd, + path, + args, + env_vars, + flags + )) +} + +pub(crate) unsafe fn execve( + path: &CStr, + args: *const *const u8, + env_vars: *const *const u8, +) -> io::Errno { + ret_error(syscall_readonly!(__NR_execve, path, args, env_vars)) +} + +pub(crate) mod tls { + use super::*; + #[cfg(target_arch = "x86")] + use crate::backend::runtime::tls::UserDesc; + + #[cfg(target_arch = "x86")] + #[inline] + pub(crate) unsafe fn set_thread_area(u_info: &mut UserDesc) -> io::Result<()> { + ret(syscall!(__NR_set_thread_area, by_mut(u_info))) + } + + #[cfg(target_arch = "arm")] + #[inline] + pub(crate) unsafe fn arm_set_tls(data: *mut c::c_void) -> io::Result<()> { + ret(syscall_readonly!(__ARM_NR_set_tls, data)) + } + + #[cfg(target_arch = "x86_64")] + #[inline] + pub(crate) unsafe fn set_fs(data: *mut c::c_void) { + ret_infallible(syscall_readonly!( + __NR_arch_prctl, + c_uint(ARCH_SET_FS), + data, + zero(), + zero(), + zero() + )) + } + + #[inline] + pub(crate) unsafe fn set_tid_address(data: *mut c::c_void) -> Pid { + let tid: i32 = ret_c_int_infallible(syscall_readonly!(__NR_set_tid_address, data)); + Pid::from_raw_unchecked(tid) + } + + #[inline] + pub(crate) fn exit_thread(code: c::c_int) -> ! { + unsafe { syscall_noreturn!(__NR_exit, c_int(code)) } + } +} + +#[inline] +pub(crate) unsafe fn kernel_sigaction( + signal: Signal, + new: Option, +) -> io::Result { + let mut old = MaybeUninit::::uninit(); + let new = opt_ref(new.as_ref()); + ret(syscall!( + __NR_rt_sigaction, + signal, + new, + &mut old, + size_of::() + ))?; + Ok(old.assume_init()) +} + +#[inline] +pub(crate) unsafe fn kernel_sigaltstack(new: Option) -> io::Result { + let mut old = MaybeUninit::::uninit(); + let new = opt_ref(new.as_ref()); + ret(syscall!(__NR_sigaltstack, new, &mut old))?; + Ok(old.assume_init()) +} + +#[inline] +pub(crate) unsafe fn tkill(tid: Pid, sig: Signal) -> io::Result<()> { + ret(syscall_readonly!(__NR_tkill, tid, sig)) +} + +#[inline] +pub(crate) unsafe fn kernel_sigprocmask( + how: How, + new: Option<&KernelSigSet>, +) -> io::Result { + let mut old = MaybeUninit::::uninit(); + let new = opt_ref(new); + ret(syscall!( + __NR_rt_sigprocmask, + how, + new, + &mut old, + size_of::() + ))?; + Ok(old.assume_init()) +} + +#[inline] +pub(crate) fn kernel_sigpending() -> KernelSigSet { + let mut pending = MaybeUninit::::uninit(); + unsafe { + ret_infallible(syscall!( + __NR_rt_sigpending, + &mut pending, + size_of::() + )); + pending.assume_init() + } +} + +#[inline] +pub(crate) fn kernel_sigsuspend(set: &KernelSigSet) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_rt_sigsuspend, + by_ref(set), + size_of::() + )) + } +} + +#[inline] +pub(crate) unsafe fn kernel_sigwait(set: &KernelSigSet) -> io::Result { + Ok(Signal::from_raw_unchecked(ret_c_int(syscall_readonly!( + __NR_rt_sigtimedwait, + by_ref(set), + zero(), + zero(), + size_of::() + ))?)) +} + +#[inline] +pub(crate) unsafe fn kernel_sigwaitinfo(set: &KernelSigSet) -> io::Result { + let mut info = MaybeUninit::::uninit(); + let _signum = ret_c_int(syscall!( + __NR_rt_sigtimedwait, + by_ref(set), + &mut info, + zero(), + size_of::() + ))?; + Ok(info.assume_init()) +} + +#[inline] +pub(crate) unsafe fn kernel_sigtimedwait( + set: &KernelSigSet, + timeout: Option<&Timespec>, +) -> io::Result { + let mut info = MaybeUninit::::uninit(); + + // `rt_sigtimedwait_time64` was introduced in Linux 5.1. The old + // `rt_sigtimedwait` syscall is not y2038-compatible on 32-bit + // architectures. + #[cfg(target_pointer_width = "32")] + { + // If we don't have Linux 5.1, and the timeout fits in a + // `__kernel_old_timespec`, use plain `rt_sigtimedwait`. + // + // We do this unconditionally, rather than trying + // `rt_sigtimedwait_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"))] + { + // If we don't have a timeout, or if we can convert the timeout to + // a `__kernel_old_timespec`, the use `__NR_futex`. + 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(old_timeout) = old_timeout { + return ret_c_int(syscall!( + __NR_rt_sigtimedwait, + by_ref(set), + &mut info, + opt_ref(old_timeout.as_ref()), + size_of::() + )) + .map(|sig| { + debug_assert_eq!( + sig, + info.assume_init_ref() + .__bindgen_anon_1 + .__bindgen_anon_1 + .si_signo + ); + info.assume_init() + }); + } + } + + ret_c_int(syscall!( + __NR_rt_sigtimedwait_time64, + by_ref(set), + &mut info, + opt_ref(timeout), + size_of::() + )) + .map(|sig| { + debug_assert_eq!( + sig, + info.assume_init_ref() + .__bindgen_anon_1 + .__bindgen_anon_1 + .si_signo + ); + info.assume_init() + }) + } + + #[cfg(target_pointer_width = "64")] + { + let _signum = ret_c_int(syscall!( + __NR_rt_sigtimedwait, + by_ref(set), + &mut info, + opt_ref(timeout), + size_of::() + ))?; + Ok(info.assume_init()) + } +} + +#[inline] +pub(crate) fn exit_group(code: c::c_int) -> ! { + unsafe { syscall_noreturn!(__NR_exit_group, c_int(code)) } +} + +#[inline] +pub(crate) unsafe fn kernel_brk(addr: *mut c::c_void) -> io::Result<*mut c_void> { + // This is non-`readonly`, to prevent loads from being reordered past it. + ret_void_star(syscall!(__NR_brk, addr)) +} diff --git a/vendor/rustix/src/backend/linux_raw/runtime/tls.rs b/vendor/rustix/src/backend/linux_raw/runtime/tls.rs new file mode 100644 index 00000000..bc04a706 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/runtime/tls.rs @@ -0,0 +1,7 @@ +//! TLS utilities. + +/// For use with [`set_thread_area`]. +/// +/// [`set_thread_area`]: crate::runtime::set_thread_area +#[cfg(target_arch = "x86")] +pub type UserDesc = linux_raw_sys::general::user_desc; diff --git a/vendor/rustix/src/backend/linux_raw/shm/mod.rs b/vendor/rustix/src/backend/linux_raw/shm/mod.rs new file mode 100644 index 00000000..1e0181a9 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/shm/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/linux_raw/shm/syscalls.rs b/vendor/rustix/src/backend/linux_raw/shm/syscalls.rs new file mode 100644 index 00000000..77b96d9e --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/shm/syscalls.rs @@ -0,0 +1,46 @@ +use crate::ffi::CStr; + +use crate::backend::fs::syscalls::{open, unlink}; +use crate::backend::fs::types::{Mode, OFlags}; +use crate::fd::OwnedFd; +use crate::{io, shm}; + +const NAME_MAX: usize = 255; +const SHM_DIR: &[u8] = b"/dev/shm/"; + +fn get_shm_name(name: &CStr) -> io::Result<([u8; NAME_MAX + SHM_DIR.len() + 1], usize)> { + let name = name.to_bytes(); + + if name.len() > NAME_MAX { + return Err(io::Errno::NAMETOOLONG); + } + + let num_slashes = name.iter().take_while(|x| **x == b'/').count(); + let after_slashes = &name[num_slashes..]; + if after_slashes.is_empty() + || after_slashes == b"." + || after_slashes == b".." + || after_slashes.contains(&b'/') + { + return Err(io::Errno::INVAL); + } + + let mut path = [0; NAME_MAX + SHM_DIR.len() + 1]; + path[..SHM_DIR.len()].copy_from_slice(SHM_DIR); + path[SHM_DIR.len()..SHM_DIR.len() + name.len()].copy_from_slice(name); + Ok((path, SHM_DIR.len() + name.len() + 1)) +} + +pub(crate) fn shm_open(name: &CStr, oflags: shm::OFlags, mode: Mode) -> io::Result { + let (path, len) = get_shm_name(name)?; + open( + CStr::from_bytes_with_nul(&path[..len]).unwrap(), + OFlags::from_bits(oflags.bits()).unwrap() | OFlags::CLOEXEC, + mode, + ) +} + +pub(crate) fn shm_unlink(name: &CStr) -> io::Result<()> { + let (path, len) = get_shm_name(name)?; + unlink(CStr::from_bytes_with_nul(&path[..len]).unwrap()) +} diff --git a/vendor/rustix/src/backend/linux_raw/shm/types.rs b/vendor/rustix/src/backend/linux_raw/shm/types.rs new file mode 100644 index 00000000..e1e8fc02 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/shm/types.rs @@ -0,0 +1,30 @@ +use crate::ffi; +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: ffi::c_uint { + /// `O_CREAT` + #[doc(alias = "CREAT")] + const CREATE = linux_raw_sys::general::O_CREAT; + + /// `O_EXCL` + const EXCL = linux_raw_sys::general::O_EXCL; + + /// `O_RDONLY` + const RDONLY = linux_raw_sys::general::O_RDONLY; + + /// `O_RDWR` + const RDWR = linux_raw_sys::general::O_RDWR; + + /// `O_TRUNC` + const TRUNC = linux_raw_sys::general::O_TRUNC; + + /// + const _ = !0; + } +} diff --git a/vendor/rustix/src/backend/linux_raw/system/mod.rs b/vendor/rustix/src/backend/linux_raw/system/mod.rs new file mode 100644 index 00000000..1e0181a9 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/system/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/linux_raw/system/syscalls.rs b/vendor/rustix/src/backend/linux_raw/system/syscalls.rs new file mode 100644 index 00000000..bbee2680 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/system/syscalls.rs @@ -0,0 +1,91 @@ +//! linux_raw syscalls supporting `rustix::system`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + +use super::types::RawUname; +use crate::backend::c; +use crate::backend::conv::{c_int, ret, ret_infallible, slice}; +use crate::fd::BorrowedFd; +use crate::ffi::CStr; +use crate::io; +use crate::system::{RebootCommand, Sysinfo}; +use core::mem::MaybeUninit; + +#[inline] +pub(crate) fn uname() -> RawUname { + let mut uname = MaybeUninit::::uninit(); + unsafe { + ret_infallible(syscall!(__NR_uname, &mut uname)); + uname.assume_init() + } +} + +#[inline] +pub(crate) fn sysinfo() -> Sysinfo { + let mut info = MaybeUninit::::uninit(); + unsafe { + ret_infallible(syscall!(__NR_sysinfo, &mut info)); + info.assume_init() + } +} + +#[inline] +pub(crate) fn sethostname(name: &[u8]) -> io::Result<()> { + let (ptr, len) = slice(name); + unsafe { ret(syscall_readonly!(__NR_sethostname, ptr, len)) } +} + +#[inline] +pub(crate) fn setdomainname(name: &[u8]) -> io::Result<()> { + let (ptr, len) = slice(name); + unsafe { ret(syscall_readonly!(__NR_setdomainname, ptr, len)) } +} + +#[inline] +pub(crate) fn reboot(cmd: RebootCommand) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_reboot, + c_int(c::LINUX_REBOOT_MAGIC1), + c_int(c::LINUX_REBOOT_MAGIC2), + c_int(cmd as i32) + )) + } +} + +#[inline] +pub(crate) fn init_module(image: &[u8], param_values: &CStr) -> io::Result<()> { + let (image, len) = slice(image); + unsafe { + ret(syscall_readonly!( + __NR_init_module, + image, + len, + param_values + )) + } +} + +#[inline] +pub(crate) fn finit_module( + fd: BorrowedFd<'_>, + param_values: &CStr, + flags: c::c_int, +) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_finit_module, + fd, + param_values, + c_int(flags) + )) + } +} + +#[inline] +pub(crate) fn delete_module(name: &CStr, flags: c::c_int) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_delete_module, name, c_int(flags))) } +} diff --git a/vendor/rustix/src/backend/linux_raw/system/types.rs b/vendor/rustix/src/backend/linux_raw/system/types.rs new file mode 100644 index 00000000..92cc5278 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/system/types.rs @@ -0,0 +1,39 @@ +use crate::ffi; +use core::mem::size_of; + +/// `sysinfo` +#[non_exhaustive] +#[repr(C)] +pub struct Sysinfo { + /// Seconds since boot + pub uptime: ffi::c_long, + /// 1, 5, and 15 minute load averages + pub loads: [ffi::c_ulong; 3], + /// Total usable main memory size + pub totalram: ffi::c_ulong, + /// Available memory size + pub freeram: ffi::c_ulong, + /// Amount of shared memory + pub sharedram: ffi::c_ulong, + /// Memory used by buffers + pub bufferram: ffi::c_ulong, + /// Total swap space size + pub totalswap: ffi::c_ulong, + /// Swap space still available + pub freeswap: ffi::c_ulong, + /// Number of current processes + pub procs: ffi::c_ushort, + + pub(crate) pad: ffi::c_ushort, + + /// Total high memory size + pub totalhigh: ffi::c_ulong, + /// Available high memory size + pub freehigh: ffi::c_ulong, + /// Memory unit size in bytes + pub mem_unit: ffi::c_uint, + + pub(crate) f: [u8; 20 - 2 * size_of::() - size_of::()], +} + +pub(crate) type RawUname = linux_raw_sys::system::new_utsname; diff --git a/vendor/rustix/src/backend/linux_raw/termios/mod.rs b/vendor/rustix/src/backend/linux_raw/termios/mod.rs new file mode 100644 index 00000000..1e0181a9 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/termios/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/linux_raw/termios/syscalls.rs b/vendor/rustix/src/backend/linux_raw/termios/syscalls.rs new file mode 100644 index 00000000..07c3a3d9 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/termios/syscalls.rs @@ -0,0 +1,425 @@ +//! linux_raw syscalls supporting `rustix::termios`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + +use crate::backend::c; +use crate::backend::conv::{by_ref, c_uint, ret}; +use crate::fd::BorrowedFd; +#[cfg(feature = "alloc")] +use crate::ffi::CStr; +use crate::io; +use crate::pid::Pid; +use crate::termios::{ + speed, Action, ControlModes, InputModes, LocalModes, OptionalActions, OutputModes, + QueueSelector, SpecialCodeIndex, Termios, Winsize, +}; +#[cfg(feature = "alloc")] +#[cfg(feature = "fs")] +use crate::{fs::FileType, path::DecInt}; +use core::mem::MaybeUninit; + +#[inline] +pub(crate) fn tcgetwinsize(fd: BorrowedFd<'_>) -> io::Result { + unsafe { + let mut result = MaybeUninit::::uninit(); + ret(syscall!(__NR_ioctl, fd, c_uint(c::TIOCGWINSZ), &mut result))?; + Ok(result.assume_init()) + } +} + +#[inline] +pub(crate) fn tcgetattr(fd: BorrowedFd<'_>) -> io::Result { + let mut result = MaybeUninit::::uninit(); + + // SAFETY: This invokes the `TCGETS2` ioctl, which initializes the full + // `Termios` structure. + unsafe { + match ret(syscall!(__NR_ioctl, fd, c_uint(c::TCGETS2), &mut result)) { + Ok(()) => Ok(result.assume_init()), + + // 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), + + Err(err) => Err(err), + } + } +} + +/// Implement `tcgetattr` using the old `TCGETS` ioctl. +#[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))] +#[cold] +fn tcgetattr_fallback(fd: BorrowedFd<'_>) -> io::Result { + use core::ptr::{addr_of, addr_of_mut}; + + let mut result = MaybeUninit::::uninit(); + + // SAFETY: This invokes the `TCGETS` ioctl which 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 { + // Do the old `TCGETS` call. + ret(syscall!(__NR_ioctl, fd, c_uint(c::TCGETS), &mut result))?; + + // Read the `control_modes` field without forming a reference to the + // `Termios` because it isn't fully initialized yet. + let ptr = result.as_mut_ptr(); + let control_modes = addr_of!((*ptr).control_modes).read(); + + // Infer the output speed and set `output_speed`. + let encoded_out = control_modes.bits() & 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).output_speed).write(output_speed); + + // Infer the input speed and set `input_speed`. `B0` is a special-case + // that means the input speed is the same as the output speed. + let encoded_in = (control_modes.bits() & 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).input_speed).write(input_speed); + + // Now all the fields are set. + Ok(result.assume_init()) + } +} + +#[inline] +pub(crate) fn tcgetpgrp(fd: BorrowedFd<'_>) -> io::Result { + unsafe { + let mut result = MaybeUninit::::uninit(); + ret(syscall!(__NR_ioctl, fd, c_uint(c::TIOCGPGRP), &mut result))?; + let pid = result.assume_init(); + + // This doesn't appear to be documented, but it appears `tcsetpgrp` can + // succeed and set the pid to 0 if we pass it a pseudo-terminal device + // fd. For now, fail with `OPNOTSUPP`. + if pid == 0 { + return Err(io::Errno::OPNOTSUPP); + } + + Ok(Pid::from_raw_unchecked(pid)) + } +} + +#[inline] +pub(crate) fn tcsetattr( + fd: BorrowedFd<'_>, + optional_actions: OptionalActions, + termios: &Termios, +) -> io::Result<()> { + // Translate from `optional_actions` into a `TCSETS2` ioctl request code. + // On MIPS, `optional_actions` has `TCSETS` added to it. + let request = c::TCSETS2 + + if cfg!(any( + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "mips64", + target_arch = "mips64r6" + )) { + optional_actions as u32 - c::TCSETS + } else { + optional_actions as u32 + }; + + // SAFETY: This invokes the `TCSETS2` ioctl. + unsafe { + match ret(syscall_readonly!( + __NR_ioctl, + fd, + c_uint(request), + by_ref(termios) + )) { + 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, termios) + } + + Err(err) => Err(err), + } + } +} + +/// Implement `tcsetattr` using the old `TCSETS` ioctl. +#[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))] +#[cold] +fn tcsetattr_fallback( + fd: BorrowedFd<'_>, + optional_actions: OptionalActions, + termios: &Termios, +) -> 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 control_modes_bits = termios.control_modes.bits(); + let encoded_out = control_modes_bits & c::CBAUD; + let encoded_in = (control_modes_bits & 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 u32 + } else { + optional_actions as u32 + c::TCSETS + }; + + // SAFETY: This invokes the `TCSETS` ioctl. + unsafe { + ret(syscall_readonly!( + __NR_ioctl, + fd, + c_uint(request), + by_ref(termios) + )) + } +} + +#[inline] +pub(crate) fn tcsendbreak(fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_ioctl, + fd, + c_uint(c::TCSBRK), + c_uint(0) + )) + } +} + +#[inline] +pub(crate) fn tcdrain(fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_ioctl, + fd, + c_uint(c::TCSBRK), + c_uint(1) + )) + } +} + +#[inline] +pub(crate) fn tcflush(fd: BorrowedFd<'_>, queue_selector: QueueSelector) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_ioctl, + fd, + c_uint(c::TCFLSH), + c_uint(queue_selector as u32) + )) + } +} + +#[inline] +pub(crate) fn tcflow(fd: BorrowedFd<'_>, action: Action) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_ioctl, + fd, + c_uint(c::TCXONC), + c_uint(action as u32) + )) + } +} + +#[inline] +pub(crate) fn tcgetsid(fd: BorrowedFd<'_>) -> io::Result { + unsafe { + let mut result = MaybeUninit::::uninit(); + ret(syscall!(__NR_ioctl, fd, c_uint(c::TIOCGSID), &mut result))?; + let pid = result.assume_init(); + Ok(Pid::from_raw_unchecked(pid)) + } +} + +#[inline] +pub(crate) fn tcsetwinsize(fd: BorrowedFd<'_>, winsize: Winsize) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_ioctl, + fd, + c_uint(c::TIOCSWINSZ), + by_ref(&winsize) + )) + } +} + +#[inline] +pub(crate) fn tcsetpgrp(fd: BorrowedFd<'_>, pid: Pid) -> io::Result<()> { + let raw_pid: c::c_int = pid.as_raw_nonzero().get(); + unsafe { + ret(syscall_readonly!( + __NR_ioctl, + fd, + c_uint(c::TIOCSPGRP), + by_ref(&raw_pid) + )) + } +} + +/// A wrapper around a conceptual `cfsetspeed` which handles an arbitrary +/// integer speed value. +#[inline] +pub(crate) fn set_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> { + let encoded_speed = speed::encode(arbitrary_speed).unwrap_or(c::BOTHER); + + 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(()) +} + +/// A wrapper around a conceptual `cfsetospeed` which handles an arbitrary +/// integer speed value. +#[inline] +pub(crate) fn set_output_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> { + let encoded_speed = speed::encode(arbitrary_speed).unwrap_or(c::BOTHER); + + 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(()) +} + +/// A wrapper around a conceptual `cfsetispeed` which handles an arbitrary +/// integer speed value. +#[inline] +pub(crate) fn set_input_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> { + let encoded_speed = speed::encode(arbitrary_speed).unwrap_or(c::BOTHER); + + 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(()) +} + +#[inline] +pub(crate) fn cfmakeraw(termios: &mut Termios) { + // From the Linux [`cfmakeraw` manual page]: + // + // [`cfmakeraw` manual page]: https://man7.org/linux/man-pages/man3/cfmakeraw.3.html + termios.input_modes -= InputModes::IGNBRK + | InputModes::BRKINT + | InputModes::PARMRK + | InputModes::ISTRIP + | InputModes::INLCR + | InputModes::IGNCR + | InputModes::ICRNL + | InputModes::IXON; + termios.output_modes -= OutputModes::OPOST; + termios.local_modes -= LocalModes::ECHO + | LocalModes::ECHONL + | LocalModes::ICANON + | LocalModes::ISIG + | LocalModes::IEXTEN; + termios.control_modes -= ControlModes::CSIZE | ControlModes::PARENB; + termios.control_modes |= ControlModes::CS8; + + // Musl and glibc also do these: + termios.special_codes[SpecialCodeIndex::VMIN] = 1; + termios.special_codes[SpecialCodeIndex::VTIME] = 0; +} + +#[inline] +pub(crate) fn isatty(fd: BorrowedFd<'_>) -> bool { + // On error, Linux will return either `EINVAL` (2.6.32) or `ENOTTY` + // (otherwise), because we assume we're never passing an invalid + // file descriptor (which would get `EBADF`). Either way, an error + // means we don't have a tty. + tcgetwinsize(fd).is_ok() +} + +#[cfg(feature = "alloc")] +#[cfg(feature = "fs")] +pub(crate) fn ttyname(fd: BorrowedFd<'_>, buf: &mut [MaybeUninit]) -> io::Result { + let fd_stat = crate::backend::fs::syscalls::fstat(fd)?; + + // Quick check: if `fd` isn't a character device, it's not a tty. + if FileType::from_raw_mode(fd_stat.st_mode) != FileType::CharacterDevice { + return Err(io::Errno::NOTTY); + } + + // Check that `fd` is really a tty. + tcgetwinsize(fd)?; + + // Create the "/proc/self/fd/" string. + let mut proc_self_fd_buf: [u8; 25] = *b"/proc/self/fd/\0\0\0\0\0\0\0\0\0\0\0"; + let dec_int = DecInt::from_fd(fd); + let bytes_with_nul = dec_int.as_bytes_with_nul(); + proc_self_fd_buf[b"/proc/self/fd/".len()..][..bytes_with_nul.len()] + .copy_from_slice(bytes_with_nul); + + // SAFETY: We just wrote a valid C String. + let proc_self_fd_path = unsafe { CStr::from_ptr(proc_self_fd_buf.as_ptr().cast()) }; + + let ptr = buf.as_mut_ptr(); + let len = { + // Gather the ttyname by reading the "fd" file inside `proc_self_fd`. + let (init, uninit) = crate::fs::readlinkat_raw(crate::fs::CWD, proc_self_fd_path, buf)?; + + // If the number of bytes is equal to the buffer length, truncation may + // have occurred. This check also ensures that we have enough space for + // adding a NUL terminator. + if uninit.is_empty() { + return Err(io::Errno::RANGE); + } + + // `readlinkat` returns the number of bytes placed in the buffer. + // NUL-terminate the string at that offset. + uninit[0].write(b'\0'); + + init.len() + }; + + // Check that the path we read refers to the same file as `fd`. + { + // SAFETY: We just wrote the NUL byte above. + let path = unsafe { CStr::from_ptr(ptr.cast()) }; + + let path_stat = crate::backend::fs::syscalls::stat(path)?; + if path_stat.st_dev != fd_stat.st_dev || path_stat.st_ino != fd_stat.st_ino { + return Err(io::Errno::NODEV); + } + } + + // Return the length, excluding the NUL terminator. + Ok(len) +} diff --git a/vendor/rustix/src/backend/linux_raw/termios/types.rs b/vendor/rustix/src/backend/linux_raw/termios/types.rs new file mode 100644 index 00000000..a8aaee39 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/termios/types.rs @@ -0,0 +1,13 @@ +//! Types for the `termios` module. + +#![allow(non_camel_case_types)] + +use crate::ffi; + +// We don't want to use `tcflag_t` directly so we don't expose linux_raw_sys +// publicly. It appears to be `c_ulong `on SPARC and `c_uint` everywhere else. + +#[cfg(target_arch = "sparc")] +pub type tcflag_t = ffi::c_ulong; +#[cfg(not(target_arch = "sparc"))] +pub type tcflag_t = ffi::c_uint; diff --git a/vendor/rustix/src/backend/linux_raw/thread/cpu_set.rs b/vendor/rustix/src/backend/linux_raw/thread/cpu_set.rs new file mode 100644 index 00000000..8c39d57c --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/thread/cpu_set.rs @@ -0,0 +1,51 @@ +//! Rust implementation of the `CPU_*` macro API. + +#![allow(non_snake_case)] + +use super::types::RawCpuSet; +use core::mem::{size_of, size_of_val}; + +#[inline] +pub(crate) fn CPU_SET(cpu: usize, cpuset: &mut RawCpuSet) { + let size_in_bits = 8 * size_of_val(&cpuset.bits[0]); // 32, 64 etc + let (idx, offset) = (cpu / size_in_bits, cpu % size_in_bits); + cpuset.bits[idx] |= 1 << offset +} + +#[inline] +pub(crate) fn CPU_ZERO(cpuset: &mut RawCpuSet) { + cpuset.bits.fill(0) +} + +#[inline] +pub(crate) fn CPU_CLR(cpu: usize, cpuset: &mut RawCpuSet) { + let size_in_bits = 8 * size_of_val(&cpuset.bits[0]); // 32, 64 etc + let (idx, offset) = (cpu / size_in_bits, cpu % size_in_bits); + cpuset.bits[idx] &= !(1 << offset) +} + +#[inline] +pub(crate) fn CPU_ISSET(cpu: usize, cpuset: &RawCpuSet) -> bool { + let size_in_bits = 8 * size_of_val(&cpuset.bits[0]); + let (idx, offset) = (cpu / size_in_bits, cpu % size_in_bits); + (cpuset.bits[idx] & (1 << offset)) != 0 +} + +#[inline] +pub(crate) fn CPU_COUNT_S(size_in_bytes: usize, cpuset: &RawCpuSet) -> u32 { + let size_of_mask = size_of_val(&cpuset.bits[0]); + let idx = size_in_bytes / size_of_mask; + cpuset.bits[..idx] + .iter() + .fold(0, |acc, i| acc + i.count_ones()) +} + +#[inline] +pub(crate) fn CPU_COUNT(cpuset: &RawCpuSet) -> u32 { + CPU_COUNT_S(size_of::(), cpuset) +} + +#[inline] +pub(crate) fn CPU_EQUAL(this: &RawCpuSet, that: &RawCpuSet) -> bool { + this.bits == that.bits +} diff --git a/vendor/rustix/src/backend/linux_raw/thread/futex.rs b/vendor/rustix/src/backend/linux_raw/thread/futex.rs new file mode 100644 index 00000000..726cea11 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/thread/futex.rs @@ -0,0 +1,89 @@ +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 = linux_raw_sys::general::FUTEX_PRIVATE_FLAG; + /// `FUTEX_CLOCK_REALTIME` + const CLOCK_REALTIME = linux_raw_sys::general::FUTEX_CLOCK_REALTIME; + + /// + 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; + + /// + 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 = linux_raw_sys::general::FUTEX_WAIT, + /// `FUTEX_WAKE` + Wake = linux_raw_sys::general::FUTEX_WAKE, + /// `FUTEX_FD` + Fd = linux_raw_sys::general::FUTEX_FD, + /// `FUTEX_REQUEUE` + Requeue = linux_raw_sys::general::FUTEX_REQUEUE, + /// `FUTEX_CMP_REQUEUE` + CmpRequeue = linux_raw_sys::general::FUTEX_CMP_REQUEUE, + /// `FUTEX_WAKE_OP` + WakeOp = linux_raw_sys::general::FUTEX_WAKE_OP, + /// `FUTEX_LOCK_PI` + LockPi = linux_raw_sys::general::FUTEX_LOCK_PI, + /// `FUTEX_UNLOCK_PI` + UnlockPi = linux_raw_sys::general::FUTEX_UNLOCK_PI, + /// `FUTEX_TRYLOCK_PI` + TrylockPi = linux_raw_sys::general::FUTEX_TRYLOCK_PI, + /// `FUTEX_WAIT_BITSET` + WaitBitset = linux_raw_sys::general::FUTEX_WAIT_BITSET, + /// `FUTEX_WAKE_BITSET` + WakeBitset = linux_raw_sys::general::FUTEX_WAKE_BITSET, + /// `FUTEX_WAIT_REQUEUE_PI` + WaitRequeuePi = linux_raw_sys::general::FUTEX_WAIT_REQUEUE_PI, + /// `FUTEX_CMP_REQUEUE_PI` + CmpRequeuePi = linux_raw_sys::general::FUTEX_CMP_REQUEUE_PI, + /// `FUTEX_LOCK_PI2` + LockPi2 = linux_raw_sys::general::FUTEX_LOCK_PI2, +} + +/// `FUTEX_WAITERS` +pub const WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS; + +/// `FUTEX_OWNER_DIED` +pub const OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED; diff --git a/vendor/rustix/src/backend/linux_raw/thread/mod.rs b/vendor/rustix/src/backend/linux_raw/thread/mod.rs new file mode 100644 index 00000000..c1843edd --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/thread/mod.rs @@ -0,0 +1,4 @@ +pub(crate) mod cpu_set; +pub(crate) mod futex; +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/linux_raw/thread/syscalls.rs b/vendor/rustix/src/backend/linux_raw/thread/syscalls.rs new file mode 100644 index 00000000..352e0615 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/thread/syscalls.rs @@ -0,0 +1,549 @@ +//! linux_raw syscalls supporting `rustix::thread`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + +use super::types::RawCpuSet; +use crate::backend::c; +use crate::backend::conv::{ + by_mut, by_ref, c_int, c_uint, opt_ref, ret, ret_c_int, ret_c_int_infallible, ret_c_uint, + ret_usize, size_of, slice, slice_just_addr, slice_just_addr_mut, zero, +}; +use crate::fd::BorrowedFd; +use crate::io; +use crate::pid::Pid; +use crate::thread::{ + futex, ClockId, Cpuid, MembarrierCommand, MembarrierQuery, NanosleepRelativeResult, Timespec, +}; +use crate::utils::as_mut_ptr; +use core::mem::MaybeUninit; +use core::sync::atomic::AtomicU32; +#[cfg(target_pointer_width = "32")] +use linux_raw_sys::general::timespec as __kernel_old_timespec; +use linux_raw_sys::general::{membarrier_cmd, membarrier_cmd_flag, TIMER_ABSTIME}; + +#[inline] +pub(crate) fn clock_nanosleep_relative(id: ClockId, req: &Timespec) -> NanosleepRelativeResult { + #[cfg(target_pointer_width = "32")] + unsafe { + let mut rem = MaybeUninit::::uninit(); + match ret(syscall!( + __NR_clock_nanosleep_time64, + id, + c_int(0), + by_ref(req), + &mut rem + )) + .or_else(|err| { + // See the comments in `clock_gettime_via_syscall` about emulation. + if err == io::Errno::NOSYS { + clock_nanosleep_relative_old(id, req, &mut rem) + } else { + Err(err) + } + }) { + Ok(()) => NanosleepRelativeResult::Ok, + Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(rem.assume_init()), + Err(err) => NanosleepRelativeResult::Err(err), + } + } + #[cfg(target_pointer_width = "64")] + unsafe { + let mut rem = MaybeUninit::::uninit(); + match ret(syscall!( + __NR_clock_nanosleep, + id, + c_int(0), + by_ref(req), + &mut rem + )) { + Ok(()) => NanosleepRelativeResult::Ok, + Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(rem.assume_init()), + Err(err) => NanosleepRelativeResult::Err(err), + } + } +} + +#[cfg(target_pointer_width = "32")] +unsafe fn clock_nanosleep_relative_old( + id: ClockId, + req: &Timespec, + rem: &mut MaybeUninit, +) -> io::Result<()> { + let old_req = __kernel_old_timespec { + tv_sec: req.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, + tv_nsec: req.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?, + }; + let mut old_rem = MaybeUninit::<__kernel_old_timespec>::uninit(); + ret(syscall!( + __NR_clock_nanosleep, + id, + c_int(0), + by_ref(&old_req), + &mut old_rem + ))?; + let old_rem = old_rem.assume_init(); + rem.write(Timespec { + tv_sec: old_rem.tv_sec.into(), + tv_nsec: old_rem.tv_nsec.into(), + }); + Ok(()) +} + +#[inline] +pub(crate) fn clock_nanosleep_absolute(id: ClockId, req: &Timespec) -> io::Result<()> { + #[cfg(target_pointer_width = "32")] + unsafe { + ret(syscall_readonly!( + __NR_clock_nanosleep_time64, + id, + c_uint(TIMER_ABSTIME), + by_ref(req), + zero() + )) + .or_else(|err| { + // See the comments in `clock_gettime_via_syscall` about emulation. + if err == io::Errno::NOSYS { + clock_nanosleep_absolute_old(id, req) + } else { + Err(err) + } + }) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret(syscall_readonly!( + __NR_clock_nanosleep, + id, + c_uint(TIMER_ABSTIME), + by_ref(req), + zero() + )) + } +} + +#[cfg(target_pointer_width = "32")] +unsafe fn clock_nanosleep_absolute_old(id: ClockId, req: &Timespec) -> io::Result<()> { + let old_req = __kernel_old_timespec { + tv_sec: req.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, + tv_nsec: req.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?, + }; + ret(syscall_readonly!( + __NR_clock_nanosleep, + id, + c_int(0), + by_ref(&old_req), + zero() + )) +} + +#[inline] +pub(crate) fn nanosleep(req: &Timespec) -> NanosleepRelativeResult { + #[cfg(target_pointer_width = "32")] + unsafe { + let mut rem = MaybeUninit::::uninit(); + match ret(syscall!( + __NR_clock_nanosleep_time64, + ClockId::Realtime, + c_int(0), + by_ref(req), + &mut rem + )) + .or_else(|err| { + // See the comments in `clock_gettime_via_syscall` about emulation. + if err == io::Errno::NOSYS { + nanosleep_old(req, &mut rem) + } else { + Err(err) + } + }) { + Ok(()) => NanosleepRelativeResult::Ok, + Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(rem.assume_init()), + Err(err) => NanosleepRelativeResult::Err(err), + } + } + #[cfg(target_pointer_width = "64")] + unsafe { + let mut rem = MaybeUninit::::uninit(); + match ret(syscall!(__NR_nanosleep, by_ref(req), &mut rem)) { + Ok(()) => NanosleepRelativeResult::Ok, + Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(rem.assume_init()), + Err(err) => NanosleepRelativeResult::Err(err), + } + } +} + +#[cfg(target_pointer_width = "32")] +unsafe fn nanosleep_old(req: &Timespec, rem: &mut MaybeUninit) -> io::Result<()> { + let old_req = __kernel_old_timespec { + tv_sec: req.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, + tv_nsec: req.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?, + }; + let mut old_rem = MaybeUninit::<__kernel_old_timespec>::uninit(); + ret(syscall!(__NR_nanosleep, by_ref(&old_req), &mut old_rem))?; + let old_rem = old_rem.assume_init(); + rem.write(Timespec { + tv_sec: old_rem.tv_sec.into(), + tv_nsec: old_rem.tv_nsec.into(), + }); + Ok(()) +} + +#[inline] +#[must_use] +pub(crate) fn gettid() -> Pid { + unsafe { + let tid = ret_c_int_infallible(syscall_readonly!(__NR_gettid)); + Pid::from_raw_unchecked(tid) + } +} + +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. +#[inline] +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 { + // Pass `val2` in the least-significant bytes of the `timeout` argument. + // [“the kernel casts the timeout value first to unsigned long, then to + // uint32_t”], so we perform that exact conversion in reverse to create + // the pointer. + // + // [“the kernel casts the timeout value first to unsigned long, then to uint32_t”]: https://man7.org/linux/man-pages/man2/futex.2.html + let timeout = val2 as usize as *const Timespec; + + #[cfg(target_pointer_width = "32")] + { + // Linux 5.1 added `futex_time64`; if we have that, use it. We don't + // need it here, because `timeout` is just passing `val2` and not a + // real timeout, but it's nice to use `futex_time64` for consistency + // with the other futex calls that do. + #[cfg(feature = "linux_5_1")] + { + ret_usize(syscall!( + __NR_futex_time64, + uaddr, + (op, flags), + c_uint(val), + timeout, + uaddr2, + c_uint(val3) + )) + } + + // If we don't have Linux 5.1, use plain `futex`. + #[cfg(not(feature = "linux_5_1"))] + { + ret_usize(syscall!( + __NR_futex, + uaddr, + (op, flags), + c_uint(val), + timeout, + uaddr2, + c_uint(val3) + )) + } + } + #[cfg(target_pointer_width = "64")] + ret_usize(syscall!( + __NR_futex, + uaddr, + (op, flags), + c_uint(val), + timeout, + uaddr2, + c_uint(val3) + )) +} + +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. +#[inline] +pub(crate) unsafe fn futex_timeout( + uaddr: *const AtomicU32, + op: super::futex::Operation, + flags: futex::Flags, + val: u32, + timeout: Option<&Timespec>, + uaddr2: *const AtomicU32, + val3: u32, +) -> io::Result { + #[cfg(target_pointer_width = "32")] + { + // If we don't have Linux 5.1, and the timeout fits in a + // `__kernel_old_timespec`, use plain `futex`. + // + // We do this unconditionally, rather than trying `futex_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"))] + { + // If we don't have a timeout, or if we can convert the timeout to + // a `__kernel_old_timespec`, the use `__NR_futex`. + 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(old_timeout) = old_timeout { + return ret_usize(syscall!( + __NR_futex, + uaddr, + (op, flags), + c_uint(val), + opt_ref(old_timeout.as_ref()), + uaddr2, + c_uint(val3) + )); + } + } + + // We either have Linux 5.1 or the timeout didn't fit in + // `__kernel_old_timespec` so `__NR_futex_time64` will either succeed + // or fail due to our having no other options. + ret_usize(syscall!( + __NR_futex_time64, + uaddr, + (op, flags), + c_uint(val), + opt_ref(timeout), + uaddr2, + c_uint(val3) + )) + } + #[cfg(target_pointer_width = "64")] + ret_usize(syscall!( + __NR_futex, + uaddr, + (op, flags), + c_uint(val), + opt_ref(timeout), + uaddr2, + c_uint(val3) + )) +} + +#[inline] +pub(crate) fn futex_waitv( + waiters: &[futex::Wait], + flags: futex::WaitvFlags, + timeout: Option<&Timespec>, + clockid: ClockId, +) -> io::Result { + let (waiters_addr, waiters_len) = slice(waiters); + unsafe { + ret_usize(syscall!( + __NR_futex_waitv, + waiters_addr, + waiters_len, + c_uint(flags.bits()), + opt_ref(timeout), + clockid + )) + } +} + +#[inline] +pub(crate) fn setns(fd: BorrowedFd<'_>, nstype: c::c_int) -> io::Result { + unsafe { ret_c_int(syscall_readonly!(__NR_setns, fd, c_int(nstype))) } +} + +#[inline] +pub(crate) fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_unshare, flags)) } +} + +#[inline] +pub(crate) fn capget( + header: &mut linux_raw_sys::general::__user_cap_header_struct, + data: &mut [MaybeUninit], +) -> io::Result<()> { + unsafe { + ret(syscall!( + __NR_capget, + by_mut(header), + slice_just_addr_mut(data) + )) + } +} + +#[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<()> { + unsafe { ret(syscall!(__NR_capset, by_mut(header), slice_just_addr(data))) } +} + +#[inline] +pub(crate) fn setuid_thread(uid: crate::ugid::Uid) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_setuid, uid)) } +} + +#[inline] +pub(crate) fn setresuid_thread( + ruid: crate::ugid::Uid, + euid: crate::ugid::Uid, + suid: crate::ugid::Uid, +) -> io::Result<()> { + #[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc"))] + unsafe { + ret(syscall_readonly!(__NR_setresuid32, ruid, euid, suid)) + } + #[cfg(not(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc")))] + unsafe { + ret(syscall_readonly!(__NR_setresuid, ruid, euid, suid)) + } +} + +#[inline] +pub(crate) fn setgid_thread(gid: crate::ugid::Gid) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_setgid, gid)) } +} + +#[inline] +pub(crate) fn setresgid_thread( + rgid: crate::ugid::Gid, + egid: crate::ugid::Gid, + sgid: crate::ugid::Gid, +) -> io::Result<()> { + #[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc"))] + unsafe { + ret(syscall_readonly!(__NR_setresgid32, rgid, egid, sgid)) + } + #[cfg(not(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc")))] + unsafe { + ret(syscall_readonly!(__NR_setresgid, rgid, egid, sgid)) + } +} + +#[inline] +pub(crate) fn setgroups_thread(gids: &[crate::ugid::Gid]) -> io::Result<()> { + let (addr, len) = slice(gids); + unsafe { ret(syscall_readonly!(__NR_setgroups, len, addr)) } +} + +// `sched_getcpu` has special optimizations via the vDSO on some architectures. +#[cfg(any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "riscv64", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x" +))] +pub(crate) use crate::backend::vdso_wrappers::sched_getcpu; + +// `sched_getcpu` on platforms without a vDSO entry for it. +#[cfg(not(any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "riscv64", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x" +)))] +#[inline] +pub(crate) fn sched_getcpu() -> usize { + let mut cpu = MaybeUninit::::uninit(); + unsafe { + let r = ret(syscall!(__NR_getcpu, &mut cpu, zero(), zero())); + debug_assert!(r.is_ok()); + cpu.assume_init() as usize + } +} + +#[inline] +pub(crate) fn sched_getaffinity(pid: Option, cpuset: &mut RawCpuSet) -> io::Result<()> { + unsafe { + // The raw Linux syscall returns the size (in bytes) of the `cpumask_t` + // data type that is used internally by the kernel to represent the CPU + // set bit mask. + let size = ret_usize(syscall!( + __NR_sched_getaffinity, + c_int(Pid::as_raw(pid)), + size_of::(), + by_mut(&mut cpuset.bits) + ))?; + let bytes = as_mut_ptr(cpuset).cast::(); + let rest = bytes.wrapping_add(size); + // Zero every byte in the cpuset not set by the kernel. + rest.write_bytes(0, core::mem::size_of::() - size); + Ok(()) + } +} + +#[inline] +pub(crate) fn sched_setaffinity(pid: Option, cpuset: &RawCpuSet) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_sched_setaffinity, + c_int(Pid::as_raw(pid)), + size_of::(), + slice_just_addr(&cpuset.bits) + )) + } +} + +#[inline] +pub(crate) fn sched_yield() { + unsafe { + // See the documentation for [`crate::thread::sched_yield`] for why + // errors are ignored. + syscall_readonly!(__NR_sched_yield).decode_void(); + } +} + +#[inline] +pub(crate) fn membarrier_query() -> MembarrierQuery { + unsafe { + match ret_c_uint(syscall!( + __NR_membarrier, + c_int(membarrier_cmd::MEMBARRIER_CMD_QUERY as _), + c_uint(0) + )) { + Ok(query) => MembarrierQuery::from_bits_retain(query), + Err(_) => MembarrierQuery::empty(), + } + } +} + +#[inline] +pub(crate) fn membarrier(cmd: MembarrierCommand) -> io::Result<()> { + unsafe { ret(syscall!(__NR_membarrier, cmd, c_uint(0))) } +} + +#[inline] +pub(crate) fn membarrier_cpu(cmd: MembarrierCommand, cpu: Cpuid) -> io::Result<()> { + unsafe { + ret(syscall!( + __NR_membarrier, + cmd, + c_uint(membarrier_cmd_flag::MEMBARRIER_CMD_FLAG_CPU as _), + cpu + )) + } +} diff --git a/vendor/rustix/src/backend/linux_raw/thread/types.rs b/vendor/rustix/src/backend/linux_raw/thread/types.rs new file mode 100644 index 00000000..be92235e --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/thread/types.rs @@ -0,0 +1,62 @@ +use linux_raw_sys::general::membarrier_cmd; + +/// 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 +#[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 = membarrier_cmd::MEMBARRIER_CMD_GLOBAL as _, + /// `MEMBARRIER_CMD_GLOBAL_EXPEDITED` + GlobalExpedited = membarrier_cmd::MEMBARRIER_CMD_GLOBAL_EXPEDITED as _, + /// `MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED` + RegisterGlobalExpedited = membarrier_cmd::MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED as _, + /// `MEMBARRIER_CMD_PRIVATE_EXPEDITED` + PrivateExpedited = membarrier_cmd::MEMBARRIER_CMD_PRIVATE_EXPEDITED as _, + /// `MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED` + RegisterPrivateExpedited = membarrier_cmd::MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED as _, + /// `MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE` + PrivateExpeditedSyncCore = membarrier_cmd::MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE as _, + /// `MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE` + RegisterPrivateExpeditedSyncCore = + membarrier_cmd::MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE as _, + /// `MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ` (since Linux 5.10) + PrivateExpeditedRseq = membarrier_cmd::MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ as _, + /// `MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ` (since Linux 5.10) + RegisterPrivateExpeditedRseq = + membarrier_cmd::MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ as _, +} + +/// A CPU identifier as a raw integer. +pub type RawCpuid = u32; + +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub(crate) struct RawCpuSet { + #[cfg(all(target_pointer_width = "32", not(target_arch = "x86_64")))] + pub(crate) bits: [u32; 32], + #[cfg(not(all(target_pointer_width = "32", not(target_arch = "x86_64"))))] + pub(crate) bits: [u64; 16], +} + +#[inline] +pub(crate) fn raw_cpu_set_new() -> RawCpuSet { + #[cfg(all(target_pointer_width = "32", not(target_arch = "x86_64")))] + { + RawCpuSet { bits: [0; 32] } + } + #[cfg(not(all(target_pointer_width = "32", not(target_arch = "x86_64"))))] + { + RawCpuSet { bits: [0; 16] } + } +} + +pub(crate) const CPU_SETSIZE: usize = 8 * core::mem::size_of::(); diff --git a/vendor/rustix/src/backend/linux_raw/time/mod.rs b/vendor/rustix/src/backend/linux_raw/time/mod.rs new file mode 100644 index 00000000..1e0181a9 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/time/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/linux_raw/time/syscalls.rs b/vendor/rustix/src/backend/linux_raw/time/syscalls.rs new file mode 100644 index 00000000..58a969d1 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/time/syscalls.rs @@ -0,0 +1,238 @@ +//! linux_raw syscalls supporting `rustix::time`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + +use crate::backend::conv::{by_ref, ret, ret_infallible, ret_owned_fd}; +use crate::clockid::ClockId; +use crate::fd::{BorrowedFd, OwnedFd}; +use crate::io; +use crate::time::{Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags}; +use crate::timespec::Timespec; +use core::mem::MaybeUninit; +#[cfg(target_pointer_width = "32")] +use linux_raw_sys::general::itimerspec as __kernel_old_itimerspec; +#[cfg(target_pointer_width = "32")] +use linux_raw_sys::general::timespec as __kernel_old_timespec; + +// `clock_gettime` has special optimizations via the vDSO. +pub(crate) use crate::backend::vdso_wrappers::{clock_gettime, clock_gettime_dynamic}; + +#[inline] +#[must_use] +pub(crate) fn clock_getres(id: ClockId) -> Timespec { + #[cfg(target_pointer_width = "32")] + unsafe { + let mut result = MaybeUninit::::uninit(); + if let Err(err) = ret(syscall!(__NR_clock_getres_time64, id, &mut result)) { + // See the comments in `clock_gettime_via_syscall` about emulation. + debug_assert_eq!(err, io::Errno::NOSYS); + clock_getres_old(id, &mut result); + } + result.assume_init() + } + #[cfg(target_pointer_width = "64")] + unsafe { + let mut result = MaybeUninit::::uninit(); + ret_infallible(syscall!(__NR_clock_getres, id, &mut result)); + result.assume_init() + } +} + +#[cfg(target_pointer_width = "32")] +unsafe fn clock_getres_old(id: ClockId, result: &mut MaybeUninit) { + let mut old_result = MaybeUninit::<__kernel_old_timespec>::uninit(); + ret_infallible(syscall!(__NR_clock_getres, id, &mut old_result)); + let old_result = old_result.assume_init(); + result.write(Timespec { + tv_sec: old_result.tv_sec.into(), + tv_nsec: old_result.tv_nsec.into(), + }); +} + +#[inline] +pub(crate) fn clock_settime(id: ClockId, timespec: Timespec) -> io::Result<()> { + // `clock_settime64` was introduced in Linux 5.1. The old `clock_settime` + // syscall is not y2038-compatible on 32-bit architectures. + #[cfg(target_pointer_width = "32")] + unsafe { + match ret(syscall_readonly!( + __NR_clock_settime64, + id, + by_ref(×pec) + )) { + Err(io::Errno::NOSYS) => clock_settime_old(id, timespec), + otherwise => otherwise, + } + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret(syscall_readonly!(__NR_clock_settime, id, by_ref(×pec))) + } +} + +#[cfg(target_pointer_width = "32")] +unsafe fn clock_settime_old(id: ClockId, timespec: Timespec) -> io::Result<()> { + let old_timespec = __kernel_old_timespec { + tv_sec: timespec + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: timespec.tv_nsec as _, + }; + ret(syscall_readonly!( + __NR_clock_settime, + id, + by_ref(&old_timespec) + )) +} + +#[inline] +pub(crate) fn timerfd_create(clockid: TimerfdClockId, flags: TimerfdFlags) -> io::Result { + unsafe { ret_owned_fd(syscall_readonly!(__NR_timerfd_create, clockid, flags)) } +} + +#[inline] +pub(crate) fn timerfd_settime( + fd: BorrowedFd<'_>, + flags: TimerfdTimerFlags, + new_value: &Itimerspec, +) -> io::Result { + let mut result = MaybeUninit::::uninit(); + + #[cfg(target_pointer_width = "64")] + unsafe { + ret(syscall!( + __NR_timerfd_settime, + fd, + flags, + by_ref(new_value), + &mut result + ))?; + Ok(result.assume_init()) + } + + #[cfg(target_pointer_width = "32")] + unsafe { + ret(syscall!( + __NR_timerfd_settime64, + fd, + flags, + by_ref(new_value), + &mut result + )) + .or_else(|err| { + // See the comments in `clock_gettime_via_syscall` about emulation. + if err == io::Errno::NOSYS { + timerfd_settime_old(fd, flags, new_value, &mut result) + } else { + Err(err) + } + })?; + Ok(result.assume_init()) + } +} + +#[cfg(target_pointer_width = "32")] +unsafe fn timerfd_settime_old( + fd: BorrowedFd<'_>, + flags: TimerfdTimerFlags, + new_value: &Itimerspec, + result: &mut MaybeUninit, +) -> io::Result<()> { + let mut old_result = MaybeUninit::<__kernel_old_itimerspec>::uninit(); + + // Convert `new_value` to the old `__kernel_old_itimerspec` format. + let old_new_value = __kernel_old_itimerspec { + it_interval: __kernel_old_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: __kernel_old_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)?, + }, + }; + ret(syscall!( + __NR_timerfd_settime, + fd, + flags, + by_ref(&old_new_value), + &mut old_result + ))?; + let old_result = old_result.assume_init(); + result.write(Itimerspec { + it_interval: Timespec { + tv_sec: old_result.it_interval.tv_sec.into(), + tv_nsec: old_result.it_interval.tv_nsec.into(), + }, + it_value: Timespec { + tv_sec: old_result.it_value.tv_sec.into(), + tv_nsec: old_result.it_value.tv_nsec.into(), + }, + }); + Ok(()) +} + +#[inline] +pub(crate) fn timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result { + let mut result = MaybeUninit::::uninit(); + + #[cfg(target_pointer_width = "64")] + unsafe { + ret(syscall!(__NR_timerfd_gettime, fd, &mut result))?; + Ok(result.assume_init()) + } + + #[cfg(target_pointer_width = "32")] + unsafe { + ret(syscall!(__NR_timerfd_gettime64, fd, &mut result)).or_else(|err| { + // See the comments in `clock_gettime_via_syscall` about emulation. + if err == io::Errno::NOSYS { + timerfd_gettime_old(fd, &mut result) + } else { + Err(err) + } + })?; + Ok(result.assume_init()) + } +} + +#[cfg(target_pointer_width = "32")] +unsafe fn timerfd_gettime_old( + fd: BorrowedFd<'_>, + result: &mut MaybeUninit, +) -> io::Result<()> { + let mut old_result = MaybeUninit::<__kernel_old_itimerspec>::uninit(); + ret(syscall!(__NR_timerfd_gettime, fd, &mut old_result))?; + let old_result = old_result.assume_init(); + result.write(Itimerspec { + it_interval: Timespec { + tv_sec: old_result.it_interval.tv_sec.into(), + tv_nsec: old_result.it_interval.tv_nsec.into(), + }, + it_value: Timespec { + tv_sec: old_result.it_value.tv_sec.into(), + tv_nsec: old_result.it_value.tv_nsec.into(), + }, + }); + Ok(()) +} diff --git a/vendor/rustix/src/backend/linux_raw/time/types.rs b/vendor/rustix/src/backend/linux_raw/time/types.rs new file mode 100644 index 00000000..ec6c91f5 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/time/types.rs @@ -0,0 +1,93 @@ +use crate::ffi; +use bitflags::bitflags; + +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: ffi::c_uint { + /// `TFD_NONBLOCK` + #[doc(alias = "TFD_NONBLOCK")] + const NONBLOCK = linux_raw_sys::general::TFD_NONBLOCK; + + /// `TFD_CLOEXEC` + #[doc(alias = "TFD_CLOEXEC")] + const CLOEXEC = linux_raw_sys::general::TFD_CLOEXEC; + + /// + const _ = !0; + } +} + +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: ffi::c_uint { + /// `TFD_TIMER_ABSTIME` + #[doc(alias = "TFD_TIMER_ABSTIME")] + const ABSTIME = linux_raw_sys::general::TFD_TIMER_ABSTIME; + + /// `TFD_TIMER_CANCEL_ON_SET` + #[doc(alias = "TFD_TIMER_CANCEL_ON_SET")] + const CANCEL_ON_SET = linux_raw_sys::general::TFD_TIMER_CANCEL_ON_SET; + + /// + const _ = !0; + } +} + +/// `CLOCK_*` constants for use with [`timerfd_create`]. +/// +/// [`timerfd_create`]: crate::time::timerfd_create +#[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 = linux_raw_sys::general::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 = linux_raw_sys::general::CLOCK_MONOTONIC, + + /// `CLOCK_BOOTTIME`—Like `Monotonic`, but advances while suspended. + /// + /// This clock is similar to `Monotonic`, but does advance while the system + /// is suspended. + #[doc(alias = "CLOCK_BOOTTIME")] + Boottime = linux_raw_sys::general::CLOCK_BOOTTIME, + + /// `CLOCK_REALTIME_ALARM`—Like `Realtime`, but wakes a suspended system. + /// + /// This clock is like `Realtime`, but can wake up a suspended system. + /// + /// Use of this clock requires the `CAP_WAKE_ALARM` Linux capability. + #[doc(alias = "CLOCK_REALTIME_ALARM")] + RealtimeAlarm = linux_raw_sys::general::CLOCK_REALTIME_ALARM, + + /// `CLOCK_BOOTTIME_ALARM`—Like `Boottime`, but wakes a suspended system. + /// + /// This clock is like `Boottime`, but can wake up a suspended system. + /// + /// Use of this clock requires the `CAP_WAKE_ALARM` Linux capability. + #[doc(alias = "CLOCK_BOOTTIME_ALARM")] + BoottimeAlarm = linux_raw_sys::general::CLOCK_BOOTTIME_ALARM, +} diff --git a/vendor/rustix/src/backend/linux_raw/ugid/mod.rs b/vendor/rustix/src/backend/linux_raw/ugid/mod.rs new file mode 100644 index 00000000..ef944f04 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/ugid/mod.rs @@ -0,0 +1 @@ +pub(crate) mod syscalls; diff --git a/vendor/rustix/src/backend/linux_raw/ugid/syscalls.rs b/vendor/rustix/src/backend/linux_raw/ugid/syscalls.rs new file mode 100644 index 00000000..4aac5f24 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/ugid/syscalls.rs @@ -0,0 +1,70 @@ +//! linux_raw syscalls for UIDs and GIDs +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + +use crate::backend::c; +use crate::backend::conv::ret_usize_infallible; +use crate::ugid::{Gid, Uid}; + +#[inline] +#[must_use] +pub(crate) fn getuid() -> Uid { + #[cfg(any(target_arch = "arm", target_arch = "sparc", target_arch = "x86"))] + unsafe { + let uid = ret_usize_infallible(syscall_readonly!(__NR_getuid32)) as c::uid_t; + Uid::from_raw(uid) + } + #[cfg(not(any(target_arch = "arm", target_arch = "sparc", target_arch = "x86")))] + unsafe { + let uid = ret_usize_infallible(syscall_readonly!(__NR_getuid)) as c::uid_t; + Uid::from_raw(uid) + } +} + +#[inline] +#[must_use] +pub(crate) fn geteuid() -> Uid { + #[cfg(any(target_arch = "arm", target_arch = "sparc", target_arch = "x86"))] + unsafe { + let uid = ret_usize_infallible(syscall_readonly!(__NR_geteuid32)) as c::uid_t; + Uid::from_raw(uid) + } + #[cfg(not(any(target_arch = "arm", target_arch = "sparc", target_arch = "x86")))] + unsafe { + let uid = ret_usize_infallible(syscall_readonly!(__NR_geteuid)) as c::uid_t; + Uid::from_raw(uid) + } +} + +#[inline] +#[must_use] +pub(crate) fn getgid() -> Gid { + #[cfg(any(target_arch = "arm", target_arch = "sparc", target_arch = "x86"))] + unsafe { + let gid = ret_usize_infallible(syscall_readonly!(__NR_getgid32)) as c::gid_t; + Gid::from_raw(gid) + } + #[cfg(not(any(target_arch = "arm", target_arch = "sparc", target_arch = "x86")))] + unsafe { + let gid = ret_usize_infallible(syscall_readonly!(__NR_getgid)) as c::gid_t; + Gid::from_raw(gid) + } +} + +#[inline] +#[must_use] +pub(crate) fn getegid() -> Gid { + #[cfg(any(target_arch = "arm", target_arch = "sparc", target_arch = "x86"))] + unsafe { + let gid = ret_usize_infallible(syscall_readonly!(__NR_getegid32)) as c::gid_t; + Gid::from_raw(gid) + } + #[cfg(not(any(target_arch = "arm", target_arch = "sparc", target_arch = "x86")))] + unsafe { + let gid = ret_usize_infallible(syscall_readonly!(__NR_getegid)) as c::gid_t; + Gid::from_raw(gid) + } +} diff --git a/vendor/rustix/src/backend/linux_raw/vdso.rs b/vendor/rustix/src/backend/linux_raw/vdso.rs new file mode 100644 index 00000000..4fa1d1cc --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/vdso.rs @@ -0,0 +1,545 @@ +//! Parse the Linux vDSO. +//! +//! The following code is transliterated from +//! tools/testing/selftests/vDSO/parse_vdso.c in Linux 6.13, which is licensed +//! with Creative Commons Zero License, version 1.0, +//! available at +//! +//! It also incorporates the patch at: +//! , +//! with changes to fix the pointer arithmetic on s390x. +//! +//! # Safety +//! +//! Parsing the vDSO involves a lot of raw pointer manipulation. This +//! implementation follows Linux's reference implementation, and adds several +//! additional safety checks. +#![allow(unsafe_code)] + +use super::c; +use crate::ffi::CStr; +use crate::utils::check_raw_pointer; +use core::ffi::c_void; +use core::mem::size_of; +use core::ptr::{null, null_mut}; +use linux_raw_sys::elf::*; + +#[cfg(target_arch = "s390x")] +type ElfHashEntry = u64; +#[cfg(not(target_arch = "s390x"))] +type ElfHashEntry = u32; + +pub(super) struct Vdso { + // Load information + load_addr: *const Elf_Ehdr, + load_end: *const c_void, // the end of the `PT_LOAD` segment + pv_offset: usize, // recorded paddr - recorded vaddr + + // Symbol table + symtab: *const Elf_Sym, + symstrings: *const u8, + gnu_hash: *const u32, + bucket: *const ElfHashEntry, + chain: *const ElfHashEntry, + nbucket: ElfHashEntry, + //nchain: ElfHashEntry, + + // Version table + versym: *const u16, + verdef: *const Elf_Verdef, +} + +/// Straight from the ELF specification…and then tweaked slightly, in order to +/// avoid a few clang warnings. +/// (And then translated to Rust). +fn elf_hash(name: &CStr) -> u32 { + let mut h: u32 = 0; + for b in name.to_bytes() { + h = (h << 4).wrapping_add(u32::from(*b)); + let g = h & 0xf000_0000; + if g != 0 { + h ^= g >> 24; + } + h &= !g; + } + h +} + +fn gnu_hash(name: &CStr) -> u32 { + let mut h: u32 = 5381; + for s in name.to_bytes() { + h = h + .wrapping_add(h.wrapping_mul(32)) + .wrapping_add(u32::from(*s)); + } + h +} + +/// Create a `Vdso` value by parsing the vDSO at the `sysinfo_ehdr` address. +fn init_from_sysinfo_ehdr() -> Option { + // SAFETY: The auxv initialization code does extensive checks to ensure + // that the value we get really is an `AT_SYSINFO_EHDR` value from the + // kernel. + unsafe { + let hdr = super::param::auxv::sysinfo_ehdr(); + + // If the platform doesn't provide a `AT_SYSINFO_EHDR`, we can't locate + // the vDSO. + if hdr.is_null() { + return None; + } + + let mut vdso = Vdso { + load_addr: hdr, + load_end: hdr.cast(), + pv_offset: 0, + symtab: null(), + symstrings: null(), + gnu_hash: null(), + bucket: null(), + chain: null(), + nbucket: 0, + //nchain: 0, + versym: null(), + verdef: null(), + }; + + let hdr = &*hdr; + let pt = check_raw_pointer::(vdso.base_plus(hdr.e_phoff)? as *mut _)?.as_ptr(); + let mut dyn_: *const Elf_Dyn = null(); + let mut num_dyn = 0; + + // We need two things from the segment table: the load offset + // and the dynamic table. + let mut found_vaddr = false; + for i in 0..hdr.e_phnum { + let phdr = &*pt.add(i as usize); + if phdr.p_type == PT_LOAD && !found_vaddr { + // The segment should be readable and executable, because it + // contains the symbol table and the function bodies. + if phdr.p_flags & (PF_R | PF_X) != (PF_R | PF_X) { + return None; + } + found_vaddr = true; + vdso.load_end = vdso.base_plus(phdr.p_offset.checked_add(phdr.p_memsz)?)?; + vdso.pv_offset = phdr.p_offset.wrapping_sub(phdr.p_vaddr); + } else if phdr.p_type == PT_DYNAMIC { + // If `p_offset` is zero, it's more likely that we're looking + // at memory that has been zeroed than that the kernel has + // somehow aliased the `Ehdr` and the `Elf_Dyn` array. + if phdr.p_offset < size_of::() { + return None; + } + + dyn_ = check_raw_pointer::(vdso.base_plus(phdr.p_offset)? as *mut _)? + .as_ptr(); + num_dyn = phdr.p_memsz / size_of::(); + } else if phdr.p_type == PT_INTERP || phdr.p_type == PT_GNU_RELRO { + // Don't trust any ELF image that has an “interpreter” or + // that uses RELRO, which is likely to be a user ELF image + // rather and not the kernel vDSO. + return None; + } + } + + if !found_vaddr || dyn_.is_null() { + return None; // Failed + } + + // Fish out the useful bits of the dynamic table. + let mut hash: *const ElfHashEntry = null(); + vdso.symstrings = null(); + vdso.symtab = null(); + vdso.versym = null(); + vdso.verdef = null(); + let mut i = 0; + loop { + if i == num_dyn { + return None; + } + let d = &*dyn_.add(i); + match d.d_tag { + DT_STRTAB => { + vdso.symstrings = + check_raw_pointer::(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)? + .as_ptr(); + } + DT_SYMTAB => { + vdso.symtab = + check_raw_pointer::(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)? + .as_ptr(); + } + DT_HASH => { + hash = check_raw_pointer::( + vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _ + )? + .as_ptr(); + } + DT_GNU_HASH => { + vdso.gnu_hash = + check_raw_pointer::(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)? + .as_ptr() + } + DT_VERSYM => { + vdso.versym = + check_raw_pointer::(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)? + .as_ptr(); + } + DT_VERDEF => { + vdso.verdef = check_raw_pointer::( + vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _, + )? + .as_ptr(); + } + DT_SYMENT => { + if d.d_un.d_ptr != size_of::() { + return None; // Failed + } + } + DT_NULL => break, + _ => {} + } + i = i.checked_add(1)?; + } + // `check_raw_pointer` will have checked these pointers for null, + // however they could still be null if the expected dynamic table + // entries are absent. + if vdso.symstrings.is_null() + || vdso.symtab.is_null() + || (hash.is_null() && vdso.gnu_hash.is_null()) + { + return None; // Failed + } + + if vdso.verdef.is_null() { + vdso.versym = null(); + } + + // Parse the hash table header. + if !vdso.gnu_hash.is_null() { + vdso.nbucket = ElfHashEntry::from(*vdso.gnu_hash); + // The bucket array is located after the header (4 uint32) and the + // bloom filter (size_t array of gnu_hash[2] elements). + vdso.bucket = vdso + .gnu_hash + .add(4) + .add(size_of::() / 4 * *vdso.gnu_hash.add(2) as usize) + .cast(); + } else { + vdso.nbucket = *hash.add(0); + //vdso.nchain = *hash.add(1); + vdso.bucket = hash.add(2); + vdso.chain = hash.add(vdso.nbucket as usize + 2); + } + + // That's all we need. + Some(vdso) + } +} + +impl Vdso { + /// Parse the vDSO. + /// + /// Returns `None` if the vDSO can't be located or if it doesn't conform to + /// our expectations. + #[inline] + pub(super) fn new() -> Option { + init_from_sysinfo_ehdr() + } + + /// Check the version for a symbol. + /// + /// # Safety + /// + /// The raw pointers inside `self` must be valid. + unsafe fn match_version(&self, mut ver: u16, name: &CStr, hash: u32) -> bool { + // This is a helper function to check if the version indexed by + // ver matches name (which hashes to hash). + // + // The version definition table is a mess, and I don't know how + // to do this in better than linear time without allocating memory + // to build an index. I also don't know why the table has + // variable size entries in the first place. + // + // For added fun, I can't find a comprehensible specification of how + // to parse all the weird flags in the table. + // + // So I just parse the whole table every time. + + // First step: find the version definition + ver &= 0x7fff; // Apparently bit 15 means "hidden" + let mut def = self.verdef; + loop { + if (*def).vd_version != VER_DEF_CURRENT { + return false; // Failed + } + + if ((*def).vd_flags & VER_FLG_BASE) == 0 && ((*def).vd_ndx & 0x7fff) == ver { + break; + } + + if (*def).vd_next == 0 { + return false; // No definition. + } + + def = def + .cast::() + .add((*def).vd_next as usize) + .cast::(); + } + + // Now figure out whether it matches. + let aux = &*(def.cast::()) + .add((*def).vd_aux as usize) + .cast::(); + (*def).vd_hash == hash + && (name == CStr::from_ptr(self.symstrings.add(aux.vda_name as usize).cast())) + } + + /// Check to see if the symbol is the one we're looking for. + /// + /// # Safety + /// + /// The raw pointers inside `self` must be valid. + unsafe fn check_sym( + &self, + sym: &Elf_Sym, + i: ElfHashEntry, + name: &CStr, + version: &CStr, + ver_hash: u32, + ) -> bool { + // Check for a defined global or weak function w/ right name. + // + // Accept `STT_NOTYPE` in addition to `STT_FUNC` for the symbol + // type, for compatibility with some versions of Linux on + // PowerPC64. See [this commit] in Linux for more background. + // + // [this commit]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/tools/testing/selftests/vDSO/parse_vdso.c?id=0161bd38c24312853ed5ae9a425a1c41c4ac674a + if ELF_ST_TYPE(sym.st_info) != STT_FUNC && ELF_ST_TYPE(sym.st_info) != STT_NOTYPE { + return false; + } + if ELF_ST_BIND(sym.st_info) != STB_GLOBAL && ELF_ST_BIND(sym.st_info) != STB_WEAK { + return false; + } + if name != CStr::from_ptr(self.symstrings.add(sym.st_name as usize).cast()) { + return false; + } + + // Check symbol version. + if !self.versym.is_null() + && !self.match_version(*self.versym.add(i as usize), version, ver_hash) + { + return false; + } + + true + } + + /// Look up a symbol in the vDSO. + pub(super) fn sym(&self, version: &CStr, name: &CStr) -> *mut c::c_void { + let ver_hash = elf_hash(version); + + // SAFETY: The pointers in `self` must be valid. + unsafe { + if !self.gnu_hash.is_null() { + let mut h1: u32 = gnu_hash(name); + + // Changes to fix the pointer arithmetic on s390x: cast + // `self.bucket` to `*const u32` here, because even though + // s390x's `ElfHashEntry` is 64-bit for `DT_HASH` tables, + // it uses 32-bit entries for `DT_GNU_HASH` tables. + let mut i = *self + .bucket + .cast::() + .add((ElfHashEntry::from(h1) % self.nbucket) as usize); + if i == 0 { + return null_mut(); + } + h1 |= 1; + // Changes to fix the pointer arithmetic on s390x: As above, + // cast `self.bucket` to `*const u32`. + let mut hashval = self + .bucket + .cast::() + .add(self.nbucket as usize) + .add((i - *self.gnu_hash.add(1)) as usize); + loop { + let sym: &Elf_Sym = &*self.symtab.add(i as usize); + let h2 = *hashval; + hashval = hashval.add(1); + if h1 == (h2 | 1) + && self.check_sym(sym, ElfHashEntry::from(i), name, version, ver_hash) + { + let sum = self.addr_from_elf(sym.st_value).unwrap(); + assert!( + sum as usize >= self.load_addr as usize + && sum as usize <= self.load_end as usize + ); + return sum as *mut c::c_void; + } + if (h2 & 1) != 0 { + break; + } + i += 1; + } + } else { + let mut i = *self + .bucket + .add((ElfHashEntry::from(elf_hash(name)) % self.nbucket) as usize); + while i != 0 { + let sym: &Elf_Sym = &*self.symtab.add(i as usize); + if sym.st_shndx != SHN_UNDEF && self.check_sym(sym, i, name, version, ver_hash) + { + let sum = self.addr_from_elf(sym.st_value).unwrap(); + assert!( + sum as usize >= self.load_addr as usize + && sum as usize <= self.load_end as usize + ); + return sum as *mut c::c_void; + } + i = *self.chain.add(i as usize); + } + } + } + + null_mut() + } + + /// Add the given address to the vDSO base address. + unsafe fn base_plus(&self, offset: usize) -> Option<*const c_void> { + // Check for overflow. + let _ = (self.load_addr as usize).checked_add(offset)?; + // Add the offset to the base. + Some(self.load_addr.cast::().add(offset).cast()) + } + + /// Translate an ELF-address-space address into a usable virtual address. + unsafe fn addr_from_elf(&self, elf_addr: usize) -> Option<*const c_void> { + self.base_plus(elf_addr.wrapping_add(self.pv_offset)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // Disable on MIPS since QEMU on MIPS doesn't provide a vDSO. + #[cfg(linux_raw)] + #[test] + #[cfg_attr(any(target_arch = "mips", target_arch = "mips64"), ignore)] + #[allow(unused_variables)] + fn test_vdso() { + let vdso = Vdso::new().unwrap(); + assert!(!vdso.symtab.is_null()); + assert!(!vdso.symstrings.is_null()); + + { + #[cfg(target_arch = "x86_64")] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime")); + #[cfg(target_arch = "arm")] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime64")); + #[cfg(target_arch = "aarch64")] + let ptr = vdso.sym(cstr!("LINUX_2.6.39"), cstr!("__kernel_clock_gettime")); + #[cfg(target_arch = "x86")] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime64")); + #[cfg(target_arch = "riscv64")] + let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__vdso_clock_gettime")); + #[cfg(target_arch = "powerpc")] + let _ptr = vdso.sym(cstr!("LINUX_5.11"), cstr!("__kernel_clock_gettime64")); + #[cfg(target_arch = "powerpc64")] + let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_clock_gettime")); + #[cfg(target_arch = "s390x")] + let ptr = vdso.sym(cstr!("LINUX_2.6.29"), cstr!("__kernel_clock_gettime")); + #[cfg(any(target_arch = "mips", target_arch = "mips32r6"))] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime64")); + #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime")); + + // On PowerPC, "__kernel_clock_gettime64" isn't available in + // Linux < 5.11. + // On x86, "__vdso_clock_gettime64" isn't available in + // Linux < 5.3. + #[cfg(not(any(target_arch = "powerpc", target_arch = "x86")))] + assert!(!ptr.is_null()); + } + + { + #[cfg(target_arch = "x86_64")] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_getres")); + #[cfg(target_arch = "arm")] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_getres")); + #[cfg(target_arch = "aarch64")] + let ptr = vdso.sym(cstr!("LINUX_2.6.39"), cstr!("__kernel_clock_getres")); + #[cfg(target_arch = "x86")] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_getres")); + #[cfg(target_arch = "riscv64")] + let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__vdso_clock_getres")); + #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] + let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_clock_getres")); + #[cfg(target_arch = "s390x")] + let ptr = vdso.sym(cstr!("LINUX_2.6.29"), cstr!("__kernel_clock_getres")); + #[cfg(any(target_arch = "mips", target_arch = "mips32r6"))] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_getres")); + #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_getres")); + + // Some versions of Linux appear to lack "__vdso_clock_getres" on x86. + #[cfg(not(target_arch = "x86"))] + assert!(!ptr.is_null()); + } + + { + #[cfg(target_arch = "x86_64")] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_gettimeofday")); + #[cfg(target_arch = "arm")] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_gettimeofday")); + #[cfg(target_arch = "aarch64")] + let ptr = vdso.sym(cstr!("LINUX_2.6.39"), cstr!("__kernel_gettimeofday")); + #[cfg(target_arch = "x86")] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_gettimeofday")); + #[cfg(target_arch = "riscv64")] + let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__vdso_gettimeofday")); + #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] + let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_gettimeofday")); + #[cfg(target_arch = "s390x")] + let ptr = vdso.sym(cstr!("LINUX_2.6.29"), cstr!("__kernel_gettimeofday")); + #[cfg(any(target_arch = "mips", target_arch = "mips32r6"))] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_gettimeofday")); + #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_gettimeofday")); + + // Some versions of Linux appear to lack "__vdso_gettimeofday" on x86. + #[cfg(not(target_arch = "x86"))] + assert!(!ptr.is_null()); + } + + #[cfg(any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "riscv64", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x", + ))] + { + #[cfg(target_arch = "x86_64")] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_getcpu")); + #[cfg(target_arch = "x86")] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_getcpu")); + #[cfg(target_arch = "riscv64")] + let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__vdso_getcpu")); + #[cfg(target_arch = "powerpc")] + let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_getcpu")); + #[cfg(target_arch = "powerpc64")] + let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_getcpu")); + #[cfg(target_arch = "s390x")] + let ptr = vdso.sym(cstr!("LINUX_2.6.29"), cstr!("__kernel_getcpu")); + + // On PowerPC, "__kernel_getcpu" isn't available in 32-bit kernels. + // Some versions of Linux appear to lack "__vdso_getcpu" on x86. + #[cfg(not(any(target_arch = "powerpc", target_arch = "x86")))] + assert!(!ptr.is_null()); + } + } +} diff --git a/vendor/rustix/src/backend/linux_raw/vdso_wrappers.rs b/vendor/rustix/src/backend/linux_raw/vdso_wrappers.rs new file mode 100644 index 00000000..338f4548 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/vdso_wrappers.rs @@ -0,0 +1,623 @@ +//! Implement syscalls using the vDSO. +//! +//! +//! +//! # Safety +//! +//! Similar to syscalls.rs, this file performs raw system calls, and sometimes +//! passes them uninitialized memory buffers. This file also calls vDSO +//! functions. +#![allow(unsafe_code)] +#![allow(clippy::missing_transmute_annotations)] + +#[cfg(target_arch = "x86")] +use super::reg::{ArgReg, RetReg, SyscallNumber, A0, A1, A2, A3, A4, A5, R0}; +use super::vdso; +#[cfg(target_arch = "x86")] +use core::arch::global_asm; +#[cfg(feature = "thread")] +#[cfg(any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "riscv64", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x", +))] +use core::ffi::c_void; +use core::mem::transmute; +use core::ptr::null_mut; +use core::sync::atomic::AtomicPtr; +use core::sync::atomic::Ordering::Relaxed; +#[cfg(target_pointer_width = "32")] +#[cfg(feature = "time")] +use linux_raw_sys::general::timespec as __kernel_old_timespec; +#[cfg(any( + all( + feature = "thread", + any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "riscv64", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x" + ) + ), + feature = "time" +))] +use {super::c, super::conv::ret, core::mem::MaybeUninit}; +#[cfg(feature = "time")] +use { + super::conv::c_int, + crate::clockid::{ClockId, DynamicClockId}, + crate::io, + crate::timespec::Timespec, + linux_raw_sys::general::__kernel_clockid_t, +}; + +#[cfg(feature = "time")] +#[inline] +#[must_use] +pub(crate) fn clock_gettime(id: ClockId) -> Timespec { + // SAFETY: `CLOCK_GETTIME` contains either null or the address of a + // function with an ABI like libc `clock_gettime`, and calling it has the + // side effect of writing to the result buffer, and no others. + unsafe { + let mut result = MaybeUninit::::uninit(); + let callee = match transmute(CLOCK_GETTIME.load(Relaxed)) { + Some(callee) => callee, + None => init_clock_gettime(), + }; + let r0 = callee(id as c::c_int, result.as_mut_ptr()); + // The `ClockId` enum only contains clocks which never fail. It may be + // tempting to change this to `debug_assert_eq`, however they can still + // fail on uncommon kernel configs, so we leave this in place to ensure + // that we don't execute undefined behavior if they ever do fail. + assert_eq!(r0, 0); + result.assume_init() + } +} + +#[cfg(feature = "time")] +#[inline] +pub(crate) fn clock_gettime_dynamic(id: DynamicClockId<'_>) -> io::Result { + let id = match id { + DynamicClockId::Known(id) => id as __kernel_clockid_t, + + DynamicClockId::Dynamic(fd) => { + // See `FD_TO_CLOCKID` in Linux's `clock_gettime` documentation. + use crate::backend::fd::AsRawFd as _; + const CLOCKFD: i32 = 3; + ((!fd.as_raw_fd() << 3) | CLOCKFD) as __kernel_clockid_t + } + + DynamicClockId::RealtimeAlarm => c::CLOCK_REALTIME_ALARM as __kernel_clockid_t, + DynamicClockId::Tai => c::CLOCK_TAI as __kernel_clockid_t, + DynamicClockId::Boottime => c::CLOCK_BOOTTIME as __kernel_clockid_t, + DynamicClockId::BoottimeAlarm => c::CLOCK_BOOTTIME_ALARM as __kernel_clockid_t, + }; + + // SAFETY: `CLOCK_GETTIME` contains either null or the address of a + // function with an ABI like libc `clock_gettime`, and calling it has the + // side effect of writing to the result buffer, and no others. + unsafe { + const EINVAL: c::c_int = -(c::EINVAL as c::c_int); + let mut timespec = MaybeUninit::::uninit(); + let callee = match transmute(CLOCK_GETTIME.load(Relaxed)) { + Some(callee) => callee, + None => init_clock_gettime(), + }; + match callee(id, timespec.as_mut_ptr()) { + 0 => (), + EINVAL => return Err(io::Errno::INVAL), + _ => _clock_gettime_via_syscall(id, timespec.as_mut_ptr())?, + } + Ok(timespec.assume_init()) + } +} + +#[cfg(feature = "thread")] +#[cfg(any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "riscv64", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x", +))] +#[inline] +pub(crate) fn sched_getcpu() -> usize { + // SAFETY: `GETCPU` contains either null or the address of a function with + // an ABI like libc `getcpu`, and calling it has the side effect of writing + // to the result buffers, and no others. + unsafe { + let mut cpu = MaybeUninit::::uninit(); + let callee = match transmute(GETCPU.load(Relaxed)) { + Some(callee) => callee, + None => init_getcpu(), + }; + let r0 = callee(cpu.as_mut_ptr(), null_mut(), null_mut()); + debug_assert_eq!(r0, 0); + cpu.assume_init() as usize + } +} + +#[cfg(target_arch = "x86")] +pub(super) mod x86_via_vdso { + use super::{transmute, ArgReg, Relaxed, RetReg, SyscallNumber, A0, A1, A2, A3, A4, A5, R0}; + use crate::backend::arch::asm; + + #[inline] + pub(in crate::backend) unsafe fn syscall0(nr: SyscallNumber<'_>) -> RetReg { + let callee = match transmute(super::SYSCALL.load(Relaxed)) { + Some(callee) => callee, + None => super::init_syscall(), + }; + asm::indirect_syscall0(callee, nr) + } + + #[inline] + pub(in crate::backend) unsafe fn syscall1<'a>( + nr: SyscallNumber<'a>, + a0: ArgReg<'a, A0>, + ) -> RetReg { + let callee = match transmute(super::SYSCALL.load(Relaxed)) { + Some(callee) => callee, + None => super::init_syscall(), + }; + asm::indirect_syscall1(callee, nr, a0) + } + + #[inline] + pub(in crate::backend) unsafe fn syscall1_noreturn<'a>( + nr: SyscallNumber<'a>, + a0: ArgReg<'a, A0>, + ) -> ! { + let callee = match transmute(super::SYSCALL.load(Relaxed)) { + Some(callee) => callee, + None => super::init_syscall(), + }; + asm::indirect_syscall1_noreturn(callee, nr, a0) + } + + #[inline] + pub(in crate::backend) unsafe fn syscall2<'a>( + nr: SyscallNumber<'a>, + a0: ArgReg<'a, A0>, + a1: ArgReg<'a, A1>, + ) -> RetReg { + let callee = match transmute(super::SYSCALL.load(Relaxed)) { + Some(callee) => callee, + None => super::init_syscall(), + }; + asm::indirect_syscall2(callee, nr, a0, a1) + } + + #[inline] + pub(in crate::backend) unsafe fn syscall3<'a>( + nr: SyscallNumber<'a>, + a0: ArgReg<'a, A0>, + a1: ArgReg<'a, A1>, + a2: ArgReg<'a, A2>, + ) -> RetReg { + let callee = match transmute(super::SYSCALL.load(Relaxed)) { + Some(callee) => callee, + None => super::init_syscall(), + }; + asm::indirect_syscall3(callee, nr, a0, a1, a2) + } + + #[inline] + pub(in crate::backend) unsafe fn syscall4<'a>( + nr: SyscallNumber<'a>, + a0: ArgReg<'a, A0>, + a1: ArgReg<'a, A1>, + a2: ArgReg<'a, A2>, + a3: ArgReg<'a, A3>, + ) -> RetReg { + let callee = match transmute(super::SYSCALL.load(Relaxed)) { + Some(callee) => callee, + None => super::init_syscall(), + }; + asm::indirect_syscall4(callee, nr, a0, a1, a2, a3) + } + + #[inline] + pub(in crate::backend) unsafe fn syscall5<'a>( + nr: SyscallNumber<'a>, + a0: ArgReg<'a, A0>, + a1: ArgReg<'a, A1>, + a2: ArgReg<'a, A2>, + a3: ArgReg<'a, A3>, + a4: ArgReg<'a, A4>, + ) -> RetReg { + let callee = match transmute(super::SYSCALL.load(Relaxed)) { + Some(callee) => callee, + None => super::init_syscall(), + }; + asm::indirect_syscall5(callee, nr, a0, a1, a2, a3, a4) + } + + #[inline] + pub(in crate::backend) unsafe fn syscall6<'a>( + nr: SyscallNumber<'a>, + a0: ArgReg<'a, A0>, + a1: ArgReg<'a, A1>, + a2: ArgReg<'a, A2>, + a3: ArgReg<'a, A3>, + a4: ArgReg<'a, A4>, + a5: ArgReg<'a, A5>, + ) -> RetReg { + let callee = match transmute(super::SYSCALL.load(Relaxed)) { + Some(callee) => callee, + None => super::init_syscall(), + }; + asm::indirect_syscall6(callee, nr, a0, a1, a2, a3, a4, a5) + } + + // With the indirect call, it isn't meaningful to do a separate + // `_readonly` optimization. + #[allow(unused_imports)] + pub(in crate::backend) use { + syscall0 as syscall0_readonly, syscall1 as syscall1_readonly, + syscall2 as syscall2_readonly, syscall3 as syscall3_readonly, + syscall4 as syscall4_readonly, syscall5 as syscall5_readonly, + syscall6 as syscall6_readonly, + }; +} + +#[cfg(feature = "time")] +type ClockGettimeType = unsafe extern "C" fn(c::c_int, *mut Timespec) -> c::c_int; + +#[cfg(feature = "thread")] +#[cfg(any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "riscv64", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x", +))] +type GetcpuType = unsafe extern "C" fn(*mut u32, *mut u32, *mut c_void) -> c::c_int; + +/// The underlying syscall functions are only called from asm, using the +/// special syscall calling convention to pass arguments and return values, +/// which the signature here doesn't reflect. +#[cfg(target_arch = "x86")] +pub(super) type SyscallType = unsafe extern "C" fn(); + +/// Initialize `CLOCK_GETTIME` and return its value. +#[cfg(feature = "time")] +#[cold] +fn init_clock_gettime() -> ClockGettimeType { + init(); + // SAFETY: Load the function address from static storage that we just + // initialized. + unsafe { transmute(CLOCK_GETTIME.load(Relaxed)) } +} + +/// Initialize `GETCPU` and return its value. +#[cfg(feature = "thread")] +#[cfg(any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "riscv64", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x", +))] +#[cold] +fn init_getcpu() -> GetcpuType { + init(); + // SAFETY: Load the function address from static storage that we just + // initialized. + unsafe { transmute(GETCPU.load(Relaxed)) } +} + +/// Initialize `SYSCALL` and return its value. +#[cfg(target_arch = "x86")] +#[cold] +fn init_syscall() -> SyscallType { + init(); + // SAFETY: Load the function address from static storage that we just + // initialized. + unsafe { transmute(SYSCALL.load(Relaxed)) } +} + +/// `AtomicPtr` can't hold a `fn` pointer, so we use a `*` pointer to this +/// placeholder type, and cast it as needed. +struct Function; +#[cfg(feature = "time")] +static CLOCK_GETTIME: AtomicPtr = AtomicPtr::new(null_mut()); +#[cfg(feature = "thread")] +#[cfg(any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "riscv64", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x", +))] +static GETCPU: AtomicPtr = AtomicPtr::new(null_mut()); +#[cfg(target_arch = "x86")] +static SYSCALL: AtomicPtr = AtomicPtr::new(null_mut()); + +#[cfg(feature = "time")] +#[must_use] +unsafe extern "C" fn clock_gettime_via_syscall(clockid: c::c_int, res: *mut Timespec) -> c::c_int { + match _clock_gettime_via_syscall(clockid, res) { + Ok(()) => 0, + Err(err) => err.raw_os_error().wrapping_neg(), + } +} + +#[cfg(feature = "time")] +#[cfg(target_pointer_width = "32")] +unsafe fn _clock_gettime_via_syscall(clockid: c::c_int, res: *mut Timespec) -> io::Result<()> { + let r0 = syscall!(__NR_clock_gettime64, c_int(clockid), res); + match ret(r0) { + Err(io::Errno::NOSYS) => _clock_gettime_via_syscall_old(clockid, res), + otherwise => otherwise, + } +} + +#[cfg(feature = "time")] +#[cfg(target_pointer_width = "32")] +unsafe fn _clock_gettime_via_syscall_old(clockid: c::c_int, res: *mut Timespec) -> io::Result<()> { + // Ordinarily `rustix` doesn't like to emulate system calls, but in the + // case of time APIs, it's specific to Linux, specific to 32-bit + // architectures *and* specific to old kernel versions, and it's not that + // hard to fix up here, so that no other code needs to worry about this. + let mut old_result = MaybeUninit::<__kernel_old_timespec>::uninit(); + let r0 = syscall!(__NR_clock_gettime, c_int(clockid), &mut old_result); + match ret(r0) { + Ok(()) => { + let old_result = old_result.assume_init(); + *res = Timespec { + tv_sec: old_result.tv_sec.into(), + tv_nsec: old_result.tv_nsec.into(), + }; + Ok(()) + } + otherwise => otherwise, + } +} + +#[cfg(feature = "time")] +#[cfg(target_pointer_width = "64")] +unsafe fn _clock_gettime_via_syscall(clockid: c::c_int, res: *mut Timespec) -> io::Result<()> { + ret(syscall!(__NR_clock_gettime, c_int(clockid), res)) +} + +#[cfg(feature = "thread")] +#[cfg(any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "riscv64", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x", +))] +unsafe extern "C" fn getcpu_via_syscall( + cpu: *mut u32, + node: *mut u32, + unused: *mut c_void, +) -> c::c_int { + match ret(syscall!(__NR_getcpu, cpu, node, unused)) { + Ok(()) => 0, + Err(err) => err.raw_os_error().wrapping_neg(), + } +} + +#[cfg(target_arch = "x86")] +extern "C" { + /// A symbol pointing to an x86 `int 0x80` instruction. This “function” + /// is only called from assembly, and only with the x86 syscall calling + /// convention, so its signature here is not its true signature. + /// + /// This extern block and the `global_asm!` below can be replaced with + /// `#[naked]` if it's stabilized. + fn rustix_x86_int_0x80(); +} + +// This uses `.weak` so that it doesn't conflict if multiple versions of rustix +// are linked in in non-lto builds, and `.ifndef` so that it doesn't conflict +// if multiple versions of rustix are linked in in lto builds. +#[cfg(target_arch = "x86")] +global_asm!( + r#" + .ifndef rustix_x86_int_0x80 + .section .text.rustix_x86_int_0x80,"ax",@progbits + .p2align 4 + .weak rustix_x86_int_0x80 + .hidden rustix_x86_int_0x80 + .type rustix_x86_int_0x80, @function +rustix_x86_int_0x80: + .cfi_startproc + int 0x80 + ret + .cfi_endproc + .size rustix_x86_int_0x80, .-rustix_x86_int_0x80 + .endif +"# +); + +fn minimal_init() { + // Store default function addresses in static storage so that if we + // end up making any system calls while we read the vDSO, they'll work. If + // the memory happens to already be initialized, this is redundant, but not + // harmful. + #[cfg(feature = "time")] + { + CLOCK_GETTIME + .compare_exchange( + null_mut(), + clock_gettime_via_syscall as *mut Function, + Relaxed, + Relaxed, + ) + .ok(); + } + + #[cfg(feature = "thread")] + #[cfg(any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "riscv64", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x", + ))] + { + GETCPU + .compare_exchange( + null_mut(), + getcpu_via_syscall as *mut Function, + Relaxed, + Relaxed, + ) + .ok(); + } + + #[cfg(target_arch = "x86")] + { + SYSCALL + .compare_exchange( + null_mut(), + rustix_x86_int_0x80 as *mut Function, + Relaxed, + Relaxed, + ) + .ok(); + } +} + +fn init() { + minimal_init(); + + if let Some(vdso) = vdso::Vdso::new() { + #[cfg(feature = "time")] + { + // Look up the platform-specific `clock_gettime` symbol as + // documented [here], except on 32-bit platforms where we look up + // the `64`-suffixed variant and fail if we don't find it. + // + // [here]: https://man7.org/linux/man-pages/man7/vdso.7.html + #[cfg(target_arch = "x86_64")] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime")); + #[cfg(target_arch = "arm")] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime64")); + #[cfg(target_arch = "aarch64")] + let ptr = vdso.sym(cstr!("LINUX_2.6.39"), cstr!("__kernel_clock_gettime")); + #[cfg(target_arch = "x86")] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime64")); + #[cfg(target_arch = "riscv64")] + let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__vdso_clock_gettime")); + #[cfg(target_arch = "powerpc")] + let ptr = vdso.sym(cstr!("LINUX_5.11"), cstr!("__kernel_clock_gettime64")); + #[cfg(target_arch = "powerpc64")] + let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_clock_gettime")); + #[cfg(target_arch = "s390x")] + let ptr = vdso.sym(cstr!("LINUX_2.6.29"), cstr!("__kernel_clock_gettime")); + #[cfg(any(target_arch = "mips", target_arch = "mips32r6"))] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime64")); + #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime")); + + // On all 64-bit platforms, the 64-bit `clock_gettime` symbols are + // always available. + #[cfg(target_pointer_width = "64")] + let ok = true; + + // On some 32-bit platforms, the 64-bit `clock_gettime` symbols are + // not available on older kernel versions. + #[cfg(any( + target_arch = "arm", + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "powerpc", + target_arch = "x86" + ))] + let ok = !ptr.is_null(); + + if ok { + assert!(!ptr.is_null()); + + // Store the computed function addresses in static storage so + // that we don't need to compute them again (but if we do, it + // doesn't hurt anything). + CLOCK_GETTIME.store(ptr.cast(), Relaxed); + } + } + + #[cfg(feature = "thread")] + #[cfg(any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "riscv64", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x", + ))] + { + // Look up the platform-specific `getcpu` symbol as documented + // [here]. + // + // [here]: https://man7.org/linux/man-pages/man7/vdso.7.html + #[cfg(target_arch = "x86_64")] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_getcpu")); + #[cfg(target_arch = "x86")] + let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_getcpu")); + #[cfg(target_arch = "riscv64")] + let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__vdso_getcpu")); + #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] + let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_getcpu")); + #[cfg(target_arch = "s390x")] + let ptr = vdso.sym(cstr!("LINUX_2.6.29"), cstr!("__kernel_getcpu")); + + #[cfg(any( + target_arch = "x86_64", + target_arch = "riscv64", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x" + ))] + let ok = true; + + // On 32-bit x86, the symbol doesn't appear present sometimes. + #[cfg(target_arch = "x86")] + let ok = !ptr.is_null(); + + #[cfg(any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "mips64", + target_arch = "mips64r6", + ))] + let ok = false; + + if ok { + assert!(!ptr.is_null()); + + // Store the computed function addresses in static storage so + // that we don't need to compute them again (but if we do, it + // doesn't hurt anything). + GETCPU.store(ptr.cast(), Relaxed); + } + } + + // On x86, also look up the vsyscall entry point. + #[cfg(target_arch = "x86")] + { + let ptr = vdso.sym(cstr!("LINUX_2.5"), cstr!("__kernel_vsyscall")); + assert!(!ptr.is_null()); + + // As above, store the computed function addresses in + // static storage. + SYSCALL.store(ptr.cast(), Relaxed); + } + } +} -- cgit v1.2.3