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 
8 use crate::backend::conv::{ret, ret_infallible};
9 use crate::clockid::ClockId;
10 use crate::io;
11 use crate::timespec::Timespec;
12 use core::mem::MaybeUninit;
13 #[cfg(all(feature = "time", target_pointer_width = "32"))]
14 use linux_raw_sys::general::itimerspec as __kernel_old_itimerspec;
15 #[cfg(target_pointer_width = "32")]
16 use linux_raw_sys::general::timespec as __kernel_old_timespec;
17 #[cfg(feature = "time")]
18 use {
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")]
27 pub(crate) use crate::backend::vdso_wrappers::{clock_gettime, clock_gettime_dynamic};
28 
29 #[inline]
clock_getres(which_clock: ClockId) -> Timespec30 pub(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")]
clock_getres_old(which_clock: ClockId, result: &mut MaybeUninit<Timespec>)51 unsafe 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]
clock_settime(which_clock: ClockId, timespec: Timespec) -> io::Result<()>63 pub(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")]
clock_settime_old(which_clock: ClockId, timespec: Timespec) -> io::Result<()>89 unsafe 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]
timerfd_create(clockid: TimerfdClockId, flags: TimerfdFlags) -> io::Result<OwnedFd>106 pub(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]
timerfd_settime( fd: BorrowedFd<'_>, flags: TimerfdTimerFlags, new_value: &Itimerspec, ) -> io::Result<Itimerspec>112 pub(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")]
timerfd_settime_old( fd: BorrowedFd<'_>, flags: TimerfdTimerFlags, new_value: &Itimerspec, result: &mut MaybeUninit<Itimerspec>, ) -> io::Result<()>155 unsafe 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]
timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result<Itimerspec>213 pub(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")]
timerfd_gettime_old( fd: BorrowedFd<'_>, result: &mut MaybeUninit<Itimerspec>, ) -> io::Result<()>239 unsafe 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 }
258