| 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 | |