• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! linux_raw syscalls supporting `rustix::time`.
2 //!
3 //! # Safety
4 //!
5 //! See the `rustix::backend` module documentation for details.
6 #![allow(unsafe_code)]
7 #![allow(clippy::undocumented_unsafe_blocks)]
8 
9 #[cfg(feature = "time")]
10 use super::super::conv::{by_ref, ret_owned_fd};
11 use super::super::conv::{ret, ret_infallible};
12 use super::types::ClockId;
13 #[cfg(feature = "time")]
14 use crate::fd::BorrowedFd;
15 #[cfg(feature = "time")]
16 use crate::fd::OwnedFd;
17 use crate::io;
18 #[cfg(feature = "time")]
19 use crate::time::{Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags};
20 use core::mem::MaybeUninit;
21 use linux_raw_sys::general::__kernel_timespec;
22 #[cfg(feature = "time")]
23 #[cfg(target_pointer_width = "32")]
24 use {core::convert::TryInto, linux_raw_sys::general::itimerspec as __kernel_old_itimerspec};
25 #[cfg(target_pointer_width = "32")]
26 use {core::ptr, linux_raw_sys::general::timespec as __kernel_old_timespec};
27 
28 // `clock_gettime` has special optimizations via the vDSO.
29 #[cfg(feature = "time")]
30 pub(crate) use super::super::vdso_wrappers::{clock_gettime, clock_gettime_dynamic};
31 
32 #[inline]
clock_getres(which_clock: ClockId) -> __kernel_timespec33 pub(crate) fn clock_getres(which_clock: ClockId) -> __kernel_timespec {
34     #[cfg(target_pointer_width = "32")]
35     unsafe {
36         let mut result = MaybeUninit::<__kernel_timespec>::uninit();
37         if let Err(err) = ret(syscall!(__NR_clock_getres_time64, which_clock, &mut result)) {
38             // See the comments in `rustix_clock_gettime_via_syscall` about
39             // emulation.
40             debug_assert_eq!(err, io::Errno::NOSYS);
41             clock_getres_old(which_clock, &mut result);
42         }
43         result.assume_init()
44     }
45     #[cfg(target_pointer_width = "64")]
46     unsafe {
47         let mut result = MaybeUninit::<__kernel_timespec>::uninit();
48         ret_infallible(syscall!(__NR_clock_getres, which_clock, &mut result));
49         result.assume_init()
50     }
51 }
52 
53 #[cfg(target_pointer_width = "32")]
clock_getres_old(which_clock: ClockId, result: &mut MaybeUninit<__kernel_timespec>)54 unsafe fn clock_getres_old(which_clock: ClockId, result: &mut MaybeUninit<__kernel_timespec>) {
55     let mut old_result = MaybeUninit::<__kernel_old_timespec>::uninit();
56     ret_infallible(syscall!(__NR_clock_getres, which_clock, &mut old_result));
57     let old_result = old_result.assume_init();
58     // TODO: With Rust 1.55, we can use MaybeUninit::write here.
59     ptr::write(
60         result.as_mut_ptr(),
61         __kernel_timespec {
62             tv_sec: old_result.tv_sec.into(),
63             tv_nsec: old_result.tv_nsec.into(),
64         },
65     );
66 }
67 
68 #[cfg(feature = "time")]
69 #[inline]
timerfd_create(clockid: TimerfdClockId, flags: TimerfdFlags) -> io::Result<OwnedFd>70 pub(crate) fn timerfd_create(clockid: TimerfdClockId, flags: TimerfdFlags) -> io::Result<OwnedFd> {
71     unsafe { ret_owned_fd(syscall!(__NR_timerfd_create, clockid, flags)) }
72 }
73 
74 #[cfg(feature = "time")]
75 #[inline]
timerfd_settime( fd: BorrowedFd<'_>, flags: TimerfdTimerFlags, new_value: &Itimerspec, ) -> io::Result<Itimerspec>76 pub(crate) fn timerfd_settime(
77     fd: BorrowedFd<'_>,
78     flags: TimerfdTimerFlags,
79     new_value: &Itimerspec,
80 ) -> io::Result<Itimerspec> {
81     let mut result = MaybeUninit::<Itimerspec>::uninit();
82 
83     #[cfg(target_pointer_width = "64")]
84     unsafe {
85         ret(syscall!(
86             __NR_timerfd_settime,
87             fd,
88             flags,
89             by_ref(new_value),
90             &mut result
91         ))?;
92         Ok(result.assume_init())
93     }
94 
95     #[cfg(target_pointer_width = "32")]
96     unsafe {
97         ret(syscall!(
98             __NR_timerfd_settime64,
99             fd,
100             flags,
101             by_ref(new_value),
102             &mut result
103         ))
104         .or_else(|err| {
105             // See the comments in `rustix_clock_gettime_via_syscall` about
106             // emulation.
107             if err == io::Errno::NOSYS {
108                 timerfd_settime_old(fd, flags, new_value, &mut result)
109             } else {
110                 Err(err)
111             }
112         })?;
113         Ok(result.assume_init())
114     }
115 }
116 
117 #[cfg(feature = "time")]
118 #[cfg(target_pointer_width = "32")]
timerfd_settime_old( fd: BorrowedFd<'_>, flags: TimerfdTimerFlags, new_value: &Itimerspec, result: &mut MaybeUninit<Itimerspec>, ) -> io::Result<()>119 unsafe fn timerfd_settime_old(
120     fd: BorrowedFd<'_>,
121     flags: TimerfdTimerFlags,
122     new_value: &Itimerspec,
123     result: &mut MaybeUninit<Itimerspec>,
124 ) -> io::Result<()> {
125     let mut old_result = MaybeUninit::<__kernel_old_itimerspec>::uninit();
126 
127     // Convert `new_value` to the old `__kernel_old_itimerspec` format.
128     let old_new_value = __kernel_old_itimerspec {
129         it_interval: __kernel_old_timespec {
130             tv_sec: new_value
131                 .it_interval
132                 .tv_sec
133                 .try_into()
134                 .map_err(|_| io::Errno::OVERFLOW)?,
135             tv_nsec: new_value
136                 .it_interval
137                 .tv_nsec
138                 .try_into()
139                 .map_err(|_| io::Errno::INVAL)?,
140         },
141         it_value: __kernel_old_timespec {
142             tv_sec: new_value
143                 .it_value
144                 .tv_sec
145                 .try_into()
146                 .map_err(|_| io::Errno::OVERFLOW)?,
147             tv_nsec: new_value
148                 .it_value
149                 .tv_nsec
150                 .try_into()
151                 .map_err(|_| io::Errno::INVAL)?,
152         },
153     };
154     ret(syscall!(
155         __NR_timerfd_settime,
156         fd,
157         flags,
158         by_ref(&old_new_value),
159         &mut old_result
160     ))?;
161     let old_result = old_result.assume_init();
162     // TODO: With Rust 1.55, we can use MaybeUninit::write here.
163     ptr::write(
164         result.as_mut_ptr(),
165         Itimerspec {
166             it_interval: __kernel_timespec {
167                 tv_sec: old_result.it_interval.tv_sec.into(),
168                 tv_nsec: old_result.it_interval.tv_nsec.into(),
169             },
170             it_value: __kernel_timespec {
171                 tv_sec: old_result.it_value.tv_sec.into(),
172                 tv_nsec: old_result.it_value.tv_nsec.into(),
173             },
174         },
175     );
176     Ok(())
177 }
178 
179 #[cfg(feature = "time")]
180 #[inline]
timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result<Itimerspec>181 pub(crate) fn timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result<Itimerspec> {
182     let mut result = MaybeUninit::<Itimerspec>::uninit();
183 
184     #[cfg(target_pointer_width = "64")]
185     unsafe {
186         ret(syscall!(__NR_timerfd_gettime, fd, &mut result))?;
187         Ok(result.assume_init())
188     }
189 
190     #[cfg(target_pointer_width = "32")]
191     unsafe {
192         ret(syscall!(__NR_timerfd_gettime64, fd, &mut result)).or_else(|err| {
193             // See the comments in `rustix_clock_gettime_via_syscall` about
194             // emulation.
195             if err == io::Errno::NOSYS {
196                 timerfd_gettime_old(fd, &mut result)
197             } else {
198                 Err(err)
199             }
200         })?;
201         Ok(result.assume_init())
202     }
203 }
204 
205 #[cfg(feature = "time")]
206 #[cfg(target_pointer_width = "32")]
timerfd_gettime_old( fd: BorrowedFd<'_>, result: &mut MaybeUninit<Itimerspec>, ) -> io::Result<()>207 unsafe fn timerfd_gettime_old(
208     fd: BorrowedFd<'_>,
209     result: &mut MaybeUninit<Itimerspec>,
210 ) -> io::Result<()> {
211     let mut old_result = MaybeUninit::<__kernel_old_itimerspec>::uninit();
212     ret(syscall!(__NR_timerfd_gettime, fd, &mut old_result))?;
213     let old_result = old_result.assume_init();
214     // TODO: With Rust 1.55, we can use MaybeUninit::write here.
215     ptr::write(
216         result.as_mut_ptr(),
217         Itimerspec {
218             it_interval: __kernel_timespec {
219                 tv_sec: old_result.it_interval.tv_sec.into(),
220                 tv_nsec: old_result.it_interval.tv_nsec.into(),
221             },
222             it_value: __kernel_timespec {
223                 tv_sec: old_result.it_value.tv_sec.into(),
224                 tv_nsec: old_result.it_value.tv_nsec.into(),
225             },
226         },
227     );
228     Ok(())
229 }
230