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(×pec)
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(×pec)
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