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 ] |
30 | 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<__kernel_timespec> = MaybeUninit::<Timespec>::uninit(); |
45 | ret_infallible(raw:syscall!(__NR_clock_getres, which_clock, &mut result)); |
46 | result.assume_init() |
47 | } |
48 | } |
49 | |
50 | #[cfg (target_pointer_width = "32" )] |
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 ] |
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(raw: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" )] |
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 ] |
106 | pub(crate) fn timerfd_create(clockid: TimerfdClockId, flags: TimerfdFlags) -> io::Result<OwnedFd> { |
107 | unsafe { ret_owned_fd(raw:syscall_readonly!(__NR_timerfd_create, clockid, flags)) } |
108 | } |
109 | |
110 | #[cfg (feature = "time" )] |
111 | #[inline ] |
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" )] |
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 ] |
213 | pub(crate) fn timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result<Itimerspec> { |
214 | let mut result: MaybeUninit<__kernel_itimerspec> = MaybeUninit::<Itimerspec>::uninit(); |
215 | |
216 | #[cfg (target_pointer_width = "64" )] |
217 | unsafe { |
218 | ret(raw: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" )] |
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 | |