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