//! 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::{ret, ret_infallible}; use crate::clockid::ClockId; use crate::io; use crate::timespec::Timespec; use core::mem::MaybeUninit; #[cfg(all(feature = "time", 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; #[cfg(feature = "time")] use { crate::backend::conv::{by_ref, ret_owned_fd}, crate::fd::BorrowedFd, crate::fd::OwnedFd, crate::time::{Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags}, }; // `clock_gettime` has special optimizations via the vDSO. #[cfg(feature = "time")] pub(crate) use crate::backend::vdso_wrappers::{clock_gettime, clock_gettime_dynamic}; #[inline] pub(crate) fn clock_getres(which_clock: ClockId) -> Timespec { #[cfg(target_pointer_width = "32")] unsafe { let mut result = MaybeUninit::::uninit(); if let Err(err) = ret(syscall!(__NR_clock_getres_time64, which_clock, &mut result)) { // See the comments in `rustix_clock_gettime_via_syscall` about // emulation. debug_assert_eq!(err, io::Errno::NOSYS); clock_getres_old(which_clock, &mut result); } result.assume_init() } #[cfg(target_pointer_width = "64")] unsafe { let mut result = MaybeUninit::::uninit(); ret_infallible(syscall!(__NR_clock_getres, which_clock, &mut result)); result.assume_init() } } #[cfg(target_pointer_width = "32")] unsafe fn clock_getres_old(which_clock: ClockId, result: &mut MaybeUninit) { let mut old_result = MaybeUninit::<__kernel_old_timespec>::uninit(); ret_infallible(syscall!(__NR_clock_getres, which_clock, &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(), }); } #[cfg(feature = "time")] #[inline] pub(crate) fn clock_settime(which_clock: 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, which_clock, by_ref(×pec) )) { Err(io::Errno::NOSYS) => clock_settime_old(which_clock, timespec), otherwise => otherwise, } } #[cfg(target_pointer_width = "64")] unsafe { ret(syscall_readonly!( __NR_clock_settime, which_clock, by_ref(×pec) )) } } #[cfg(feature = "time")] #[cfg(target_pointer_width = "32")] unsafe fn clock_settime_old(which_clock: 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, which_clock, by_ref(&old_timespec) )) } #[cfg(feature = "time")] #[inline] pub(crate) fn timerfd_create(clockid: TimerfdClockId, flags: TimerfdFlags) -> io::Result { unsafe { ret_owned_fd(syscall_readonly!(__NR_timerfd_create, clockid, flags)) } } #[cfg(feature = "time")] #[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 `rustix_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(feature = "time")] #[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(()) } #[cfg(feature = "time")] #[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 `rustix_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(feature = "time")] #[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(()) }