rustix/backend/linux_raw/time/
syscalls.rs

1//! linux_raw syscalls supporting `rustix::time`.
2//!
3//! # Safety
4//!
5//! See the `rustix::backend` module documentation for details.
6#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)]
7
8use crate::backend::conv::{by_ref, ret, ret_infallible, ret_owned_fd};
9use crate::clockid::ClockId;
10use crate::fd::{BorrowedFd, OwnedFd};
11use crate::io;
12use crate::time::{Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags};
13use crate::timespec::Timespec;
14use core::mem::MaybeUninit;
15#[cfg(target_pointer_width = "32")]
16use linux_raw_sys::general::itimerspec as __kernel_old_itimerspec;
17#[cfg(target_pointer_width = "32")]
18use linux_raw_sys::general::timespec as __kernel_old_timespec;
19
20// `clock_gettime` has special optimizations via the vDSO.
21pub(crate) use crate::backend::vdso_wrappers::{clock_gettime, clock_gettime_dynamic};
22
23#[inline]
24#[must_use]
25pub(crate) fn clock_getres(id: ClockId) -> Timespec {
26    #[cfg(target_pointer_width = "32")]
27    unsafe {
28        let mut result = MaybeUninit::<Timespec>::uninit();
29        if let Err(err) = ret(syscall!(__NR_clock_getres_time64, id, &mut result)) {
30            // See the comments in `clock_gettime_via_syscall` about emulation.
31            debug_assert_eq!(err, io::Errno::NOSYS);
32            clock_getres_old(id, &mut result);
33        }
34        result.assume_init()
35    }
36    #[cfg(target_pointer_width = "64")]
37    unsafe {
38        let mut result = MaybeUninit::<Timespec>::uninit();
39        ret_infallible(syscall!(__NR_clock_getres, id, &mut result));
40        result.assume_init()
41    }
42}
43
44#[cfg(target_pointer_width = "32")]
45unsafe fn clock_getres_old(id: ClockId, result: &mut MaybeUninit<Timespec>) {
46    let mut old_result = MaybeUninit::<__kernel_old_timespec>::uninit();
47    ret_infallible(syscall!(__NR_clock_getres, id, &mut old_result));
48    let old_result = old_result.assume_init();
49    result.write(Timespec {
50        tv_sec: old_result.tv_sec.into(),
51        tv_nsec: old_result.tv_nsec.into(),
52    });
53}
54
55#[inline]
56pub(crate) fn clock_settime(id: ClockId, timespec: Timespec) -> io::Result<()> {
57    // `clock_settime64` was introduced in Linux 5.1. The old `clock_settime`
58    // syscall is not y2038-compatible on 32-bit architectures.
59    #[cfg(target_pointer_width = "32")]
60    unsafe {
61        match ret(syscall_readonly!(
62            __NR_clock_settime64,
63            id,
64            by_ref(&timespec)
65        )) {
66            Err(io::Errno::NOSYS) => clock_settime_old(id, timespec),
67            otherwise => otherwise,
68        }
69    }
70    #[cfg(target_pointer_width = "64")]
71    unsafe {
72        ret(syscall_readonly!(__NR_clock_settime, id, by_ref(&timespec)))
73    }
74}
75
76#[cfg(target_pointer_width = "32")]
77unsafe fn clock_settime_old(id: ClockId, timespec: Timespec) -> io::Result<()> {
78    let old_timespec = __kernel_old_timespec {
79        tv_sec: timespec
80            .tv_sec
81            .try_into()
82            .map_err(|_| io::Errno::OVERFLOW)?,
83        tv_nsec: timespec.tv_nsec as _,
84    };
85    ret(syscall_readonly!(
86        __NR_clock_settime,
87        id,
88        by_ref(&old_timespec)
89    ))
90}
91
92#[inline]
93pub(crate) fn timerfd_create(clockid: TimerfdClockId, flags: TimerfdFlags) -> io::Result<OwnedFd> {
94    unsafe { ret_owned_fd(syscall_readonly!(__NR_timerfd_create, clockid, flags)) }
95}
96
97#[inline]
98pub(crate) fn timerfd_settime(
99    fd: BorrowedFd<'_>,
100    flags: TimerfdTimerFlags,
101    new_value: &Itimerspec,
102) -> io::Result<Itimerspec> {
103    let mut result = MaybeUninit::<Itimerspec>::uninit();
104
105    #[cfg(target_pointer_width = "64")]
106    unsafe {
107        ret(syscall!(
108            __NR_timerfd_settime,
109            fd,
110            flags,
111            by_ref(new_value),
112            &mut result
113        ))?;
114        Ok(result.assume_init())
115    }
116
117    #[cfg(target_pointer_width = "32")]
118    unsafe {
119        ret(syscall!(
120            __NR_timerfd_settime64,
121            fd,
122            flags,
123            by_ref(new_value),
124            &mut result
125        ))
126        .or_else(|err| {
127            // See the comments in `clock_gettime_via_syscall` about emulation.
128            if err == io::Errno::NOSYS {
129                timerfd_settime_old(fd, flags, new_value, &mut result)
130            } else {
131                Err(err)
132            }
133        })?;
134        Ok(result.assume_init())
135    }
136}
137
138#[cfg(target_pointer_width = "32")]
139unsafe fn timerfd_settime_old(
140    fd: BorrowedFd<'_>,
141    flags: TimerfdTimerFlags,
142    new_value: &Itimerspec,
143    result: &mut MaybeUninit<Itimerspec>,
144) -> io::Result<()> {
145    let mut old_result = MaybeUninit::<__kernel_old_itimerspec>::uninit();
146
147    // Convert `new_value` to the old `__kernel_old_itimerspec` format.
148    let old_new_value = __kernel_old_itimerspec {
149        it_interval: __kernel_old_timespec {
150            tv_sec: new_value
151                .it_interval
152                .tv_sec
153                .try_into()
154                .map_err(|_| io::Errno::OVERFLOW)?,
155            tv_nsec: new_value
156                .it_interval
157                .tv_nsec
158                .try_into()
159                .map_err(|_| io::Errno::INVAL)?,
160        },
161        it_value: __kernel_old_timespec {
162            tv_sec: new_value
163                .it_value
164                .tv_sec
165                .try_into()
166                .map_err(|_| io::Errno::OVERFLOW)?,
167            tv_nsec: new_value
168                .it_value
169                .tv_nsec
170                .try_into()
171                .map_err(|_| io::Errno::INVAL)?,
172        },
173    };
174    ret(syscall!(
175        __NR_timerfd_settime,
176        fd,
177        flags,
178        by_ref(&old_new_value),
179        &mut old_result
180    ))?;
181    let old_result = old_result.assume_init();
182    result.write(Itimerspec {
183        it_interval: Timespec {
184            tv_sec: old_result.it_interval.tv_sec.into(),
185            tv_nsec: old_result.it_interval.tv_nsec.into(),
186        },
187        it_value: Timespec {
188            tv_sec: old_result.it_value.tv_sec.into(),
189            tv_nsec: old_result.it_value.tv_nsec.into(),
190        },
191    });
192    Ok(())
193}
194
195#[inline]
196pub(crate) fn timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result<Itimerspec> {
197    let mut result = MaybeUninit::<Itimerspec>::uninit();
198
199    #[cfg(target_pointer_width = "64")]
200    unsafe {
201        ret(syscall!(__NR_timerfd_gettime, fd, &mut result))?;
202        Ok(result.assume_init())
203    }
204
205    #[cfg(target_pointer_width = "32")]
206    unsafe {
207        ret(syscall!(__NR_timerfd_gettime64, fd, &mut result)).or_else(|err| {
208            // See the comments in `clock_gettime_via_syscall` about emulation.
209            if err == io::Errno::NOSYS {
210                timerfd_gettime_old(fd, &mut result)
211            } else {
212                Err(err)
213            }
214        })?;
215        Ok(result.assume_init())
216    }
217}
218
219#[cfg(target_pointer_width = "32")]
220unsafe fn timerfd_gettime_old(
221    fd: BorrowedFd<'_>,
222    result: &mut MaybeUninit<Itimerspec>,
223) -> io::Result<()> {
224    let mut old_result = MaybeUninit::<__kernel_old_itimerspec>::uninit();
225    ret(syscall!(__NR_timerfd_gettime, fd, &mut old_result))?;
226    let old_result = old_result.assume_init();
227    result.write(Itimerspec {
228        it_interval: Timespec {
229            tv_sec: old_result.it_interval.tv_sec.into(),
230            tv_nsec: old_result.it_interval.tv_nsec.into(),
231        },
232        it_value: Timespec {
233            tv_sec: old_result.it_value.tv_sec.into(),
234            tv_nsec: old_result.it_value.tv_nsec.into(),
235        },
236    });
237    Ok(())
238}