1 | //! linux_raw syscalls supporting `rustix::io`. |
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 (target_pointer_width = "64" )] |
10 | use crate::backend::conv::loff_t_from_u64; |
11 | #[cfg (all( |
12 | target_pointer_width = "32" , |
13 | any(target_arch = "arm" , target_arch = "mips" , target_arch = "mips32r6" ), |
14 | ))] |
15 | use crate::backend::conv::zero; |
16 | use crate::backend::conv::{ |
17 | c_uint, pass_usize, raw_fd, ret, ret_c_int, ret_c_uint, ret_discarded_fd, ret_owned_fd, |
18 | ret_usize, slice, |
19 | }; |
20 | #[cfg (target_pointer_width = "32" )] |
21 | use crate::backend::conv::{hi, lo}; |
22 | use crate::backend::{c, MAX_IOV}; |
23 | use crate::fd::{AsFd, BorrowedFd, OwnedFd, RawFd}; |
24 | use crate::io::{self, DupFlags, FdFlags, IoSlice, IoSliceMut, ReadWriteFlags}; |
25 | use crate::ioctl::{IoctlOutput, RawOpcode}; |
26 | #[cfg (all(feature = "fs" , feature = "net" ))] |
27 | use crate::net::{RecvFlags, SendFlags}; |
28 | use core::cmp; |
29 | use linux_raw_sys::general::{F_DUPFD_CLOEXEC, F_GETFD, F_SETFD}; |
30 | |
31 | #[inline ] |
32 | pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: *mut u8, len: usize) -> io::Result<usize> { |
33 | ret_usize(raw:syscall!(__NR_read, fd, buf, pass_usize(len))) |
34 | } |
35 | |
36 | #[inline ] |
37 | pub(crate) unsafe fn pread( |
38 | fd: BorrowedFd<'_>, |
39 | buf: *mut u8, |
40 | len: usize, |
41 | pos: u64, |
42 | ) -> io::Result<usize> { |
43 | // <https://github.com/torvalds/linux/blob/fcadab740480e0e0e9fa9bd272acd409884d431a/arch/arm64/kernel/sys32.c#L75> |
44 | #[cfg (all( |
45 | target_pointer_width = "32" , |
46 | any(target_arch = "arm" , target_arch = "mips" , target_arch = "mips32r6" ), |
47 | ))] |
48 | { |
49 | ret_usize(syscall!( |
50 | __NR_pread64, |
51 | fd, |
52 | buf, |
53 | pass_usize(len), |
54 | zero(), |
55 | hi(pos), |
56 | lo(pos) |
57 | )) |
58 | } |
59 | #[cfg (all( |
60 | target_pointer_width = "32" , |
61 | not(any(target_arch = "arm" , target_arch = "mips" , target_arch = "mips32r6" )), |
62 | ))] |
63 | { |
64 | ret_usize(syscall!( |
65 | __NR_pread64, |
66 | fd, |
67 | buf, |
68 | pass_usize(len), |
69 | hi(pos), |
70 | lo(pos) |
71 | )) |
72 | } |
73 | #[cfg (target_pointer_width = "64" )] |
74 | ret_usize(syscall!( |
75 | __NR_pread64, |
76 | fd, |
77 | buf, |
78 | pass_usize(len), |
79 | loff_t_from_u64(pos) |
80 | )) |
81 | } |
82 | |
83 | #[inline ] |
84 | pub(crate) fn readv(fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { |
85 | let (bufs_addr: ArgReg<'_, A1>, bufs_len: ArgReg<'_, A2>) = slice(&bufs[..cmp::min(v1:bufs.len(), MAX_IOV)]); |
86 | |
87 | unsafe { ret_usize(raw:syscall!(__NR_readv, fd, bufs_addr, bufs_len)) } |
88 | } |
89 | |
90 | #[inline ] |
91 | pub(crate) fn preadv( |
92 | fd: BorrowedFd<'_>, |
93 | bufs: &mut [IoSliceMut<'_>], |
94 | pos: u64, |
95 | ) -> io::Result<usize> { |
96 | let (bufs_addr: ArgReg<'_, A1>, bufs_len: ArgReg<'_, A2>) = slice(&bufs[..cmp::min(v1:bufs.len(), MAX_IOV)]); |
97 | |
98 | // Unlike the plain "p" functions, the "pv" functions pass their offset in |
99 | // an endian-independent way, and always in two registers. |
100 | unsafe { |
101 | ret_usize(raw:syscall!( |
102 | __NR_preadv, |
103 | fd, |
104 | bufs_addr, |
105 | bufs_len, |
106 | pass_usize(pos as usize), |
107 | pass_usize((pos >> 32) as usize) |
108 | )) |
109 | } |
110 | } |
111 | |
112 | #[inline ] |
113 | pub(crate) fn preadv2( |
114 | fd: BorrowedFd<'_>, |
115 | bufs: &mut [IoSliceMut<'_>], |
116 | pos: u64, |
117 | flags: ReadWriteFlags, |
118 | ) -> io::Result<usize> { |
119 | let (bufs_addr: ArgReg<'_, A1>, bufs_len: ArgReg<'_, A2>) = slice(&bufs[..cmp::min(v1:bufs.len(), MAX_IOV)]); |
120 | |
121 | // Unlike the plain "p" functions, the "pv" functions pass their offset in |
122 | // an endian-independent way, and always in two registers. |
123 | unsafe { |
124 | ret_usize(raw:syscall!( |
125 | __NR_preadv2, |
126 | fd, |
127 | bufs_addr, |
128 | bufs_len, |
129 | pass_usize(pos as usize), |
130 | pass_usize((pos >> 32) as usize), |
131 | flags |
132 | )) |
133 | } |
134 | } |
135 | |
136 | #[inline ] |
137 | pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize> { |
138 | let (buf_addr: ArgReg<'_, A1>, buf_len: ArgReg<'_, A2>) = slice(buf); |
139 | |
140 | unsafe { ret_usize(raw:syscall_readonly!(__NR_write, fd, buf_addr, buf_len)) } |
141 | } |
142 | |
143 | #[inline ] |
144 | pub(crate) fn pwrite(fd: BorrowedFd<'_>, buf: &[u8], pos: u64) -> io::Result<usize> { |
145 | let (buf_addr, buf_len) = slice(buf); |
146 | |
147 | // <https://github.com/torvalds/linux/blob/fcadab740480e0e0e9fa9bd272acd409884d431a/arch/arm64/kernel/sys32.c#L81-L83> |
148 | #[cfg (all( |
149 | target_pointer_width = "32" , |
150 | any(target_arch = "arm" , target_arch = "mips" , target_arch = "mips32r6" ), |
151 | ))] |
152 | unsafe { |
153 | ret_usize(syscall_readonly!( |
154 | __NR_pwrite64, |
155 | fd, |
156 | buf_addr, |
157 | buf_len, |
158 | zero(), |
159 | hi(pos), |
160 | lo(pos) |
161 | )) |
162 | } |
163 | #[cfg (all( |
164 | target_pointer_width = "32" , |
165 | not(any(target_arch = "arm" , target_arch = "mips" , target_arch = "mips32r6" )), |
166 | ))] |
167 | unsafe { |
168 | ret_usize(syscall_readonly!( |
169 | __NR_pwrite64, |
170 | fd, |
171 | buf_addr, |
172 | buf_len, |
173 | hi(pos), |
174 | lo(pos) |
175 | )) |
176 | } |
177 | #[cfg (target_pointer_width = "64" )] |
178 | unsafe { |
179 | ret_usize(syscall_readonly!( |
180 | __NR_pwrite64, |
181 | fd, |
182 | buf_addr, |
183 | buf_len, |
184 | loff_t_from_u64(pos) |
185 | )) |
186 | } |
187 | } |
188 | |
189 | #[inline ] |
190 | pub(crate) fn writev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>]) -> io::Result<usize> { |
191 | let (bufs_addr: ArgReg<'_, A1>, bufs_len: ArgReg<'_, A2>) = slice(&bufs[..cmp::min(v1:bufs.len(), MAX_IOV)]); |
192 | |
193 | unsafe { ret_usize(raw:syscall_readonly!(__NR_writev, fd, bufs_addr, bufs_len)) } |
194 | } |
195 | |
196 | #[inline ] |
197 | pub(crate) fn pwritev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>], pos: u64) -> io::Result<usize> { |
198 | let (bufs_addr: ArgReg<'_, A1>, bufs_len: ArgReg<'_, A2>) = slice(&bufs[..cmp::min(v1:bufs.len(), MAX_IOV)]); |
199 | |
200 | // Unlike the plain "p" functions, the "pv" functions pass their offset in |
201 | // an endian-independent way, and always in two registers. |
202 | unsafe { |
203 | ret_usize(raw:syscall_readonly!( |
204 | __NR_pwritev, |
205 | fd, |
206 | bufs_addr, |
207 | bufs_len, |
208 | pass_usize(pos as usize), |
209 | pass_usize((pos >> 32) as usize) |
210 | )) |
211 | } |
212 | } |
213 | |
214 | #[inline ] |
215 | pub(crate) fn pwritev2( |
216 | fd: BorrowedFd<'_>, |
217 | bufs: &[IoSlice<'_>], |
218 | pos: u64, |
219 | flags: ReadWriteFlags, |
220 | ) -> io::Result<usize> { |
221 | let (bufs_addr: ArgReg<'_, A1>, bufs_len: ArgReg<'_, A2>) = slice(&bufs[..cmp::min(v1:bufs.len(), MAX_IOV)]); |
222 | |
223 | // Unlike the plain "p" functions, the "pv" functions pass their offset in |
224 | // an endian-independent way, and always in two registers. |
225 | unsafe { |
226 | ret_usize(raw:syscall_readonly!( |
227 | __NR_pwritev2, |
228 | fd, |
229 | bufs_addr, |
230 | bufs_len, |
231 | pass_usize(pos as usize), |
232 | pass_usize((pos >> 32) as usize), |
233 | flags |
234 | )) |
235 | } |
236 | } |
237 | |
238 | #[inline ] |
239 | pub(crate) unsafe fn close(fd: RawFd) { |
240 | // See the documentation for [`io::close`] for why errors are ignored. |
241 | syscall_readonly!(__NR_close, raw_fd(fd)).decode_void(); |
242 | } |
243 | |
244 | #[inline ] |
245 | pub(crate) unsafe fn ioctl( |
246 | fd: BorrowedFd<'_>, |
247 | request: RawOpcode, |
248 | arg: *mut c::c_void, |
249 | ) -> io::Result<IoctlOutput> { |
250 | ret_c_int(raw:syscall!(__NR_ioctl, fd, c_uint(request), arg)) |
251 | } |
252 | |
253 | #[inline ] |
254 | pub(crate) unsafe fn ioctl_readonly( |
255 | fd: BorrowedFd<'_>, |
256 | request: RawOpcode, |
257 | arg: *mut c::c_void, |
258 | ) -> io::Result<IoctlOutput> { |
259 | ret_c_int(raw:syscall_readonly!(__NR_ioctl, fd, c_uint(request), arg)) |
260 | } |
261 | |
262 | #[cfg (all(feature = "fs" , feature = "net" ))] |
263 | pub(crate) fn is_read_write(fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> { |
264 | let (mut read, mut write) = crate::fs::fd::_is_file_read_write(fd)?; |
265 | let mut not_socket = false; |
266 | if read { |
267 | // Do a `recv` with `PEEK` and `DONTWAIT` for 1 byte. A 0 indicates |
268 | // the read side is shut down; an `EWOULDBLOCK` indicates the read |
269 | // side is still open. |
270 | let mut buf = [core::mem::MaybeUninit::<u8>::uninit()]; |
271 | match unsafe { |
272 | crate::backend::net::syscalls::recv( |
273 | fd, |
274 | buf.as_mut_ptr() as *mut u8, |
275 | 1, |
276 | RecvFlags::PEEK | RecvFlags::DONTWAIT, |
277 | ) |
278 | } { |
279 | Ok(0) => read = false, |
280 | Err(err) => { |
281 | #[allow (unreachable_patterns)] // `EAGAIN` may equal `EWOULDBLOCK` |
282 | match err { |
283 | io::Errno::AGAIN | io::Errno::WOULDBLOCK => (), |
284 | io::Errno::NOTSOCK => not_socket = true, |
285 | _ => return Err(err), |
286 | } |
287 | } |
288 | Ok(_) => (), |
289 | } |
290 | } |
291 | if write && !not_socket { |
292 | // Do a `send` with `DONTWAIT` for 0 bytes. An `EPIPE` indicates |
293 | // the write side is shut down. |
294 | #[allow (unreachable_patterns)] // `EAGAIN` equals `EWOULDBLOCK` |
295 | match crate::backend::net::syscalls::send(fd, &[], SendFlags::DONTWAIT) { |
296 | Err(io::Errno::AGAIN | io::Errno::WOULDBLOCK | io::Errno::NOTSOCK) => (), |
297 | Err(io::Errno::PIPE) => write = false, |
298 | Err(err) => return Err(err), |
299 | Ok(_) => (), |
300 | } |
301 | } |
302 | Ok((read, write)) |
303 | } |
304 | |
305 | #[inline ] |
306 | pub(crate) fn dup(fd: BorrowedFd<'_>) -> io::Result<OwnedFd> { |
307 | unsafe { ret_owned_fd(raw:syscall_readonly!(__NR_dup, fd)) } |
308 | } |
309 | |
310 | #[allow (clippy::needless_pass_by_ref_mut)] |
311 | #[inline ] |
312 | pub(crate) fn dup2(fd: BorrowedFd<'_>, new: &mut OwnedFd) -> io::Result<()> { |
313 | #[cfg (any(target_arch = "aarch64" , target_arch = "riscv64" ))] |
314 | { |
315 | // We don't need to worry about the difference between `dup2` and |
316 | // `dup3` when the file descriptors are equal because we have an |
317 | // `&mut OwnedFd` which means `fd` doesn't alias it. |
318 | dup3(fd, new, DupFlags::empty()) |
319 | } |
320 | |
321 | #[cfg (not(any(target_arch = "aarch64" , target_arch = "riscv64" )))] |
322 | unsafe { |
323 | ret_discarded_fd(raw:syscall_readonly!(__NR_dup2, fd, new.as_fd())) |
324 | } |
325 | } |
326 | |
327 | #[allow (clippy::needless_pass_by_ref_mut)] |
328 | #[inline ] |
329 | pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, flags: DupFlags) -> io::Result<()> { |
330 | unsafe { ret_discarded_fd(raw:syscall_readonly!(__NR_dup3, fd, new.as_fd(), flags)) } |
331 | } |
332 | |
333 | #[inline ] |
334 | pub(crate) fn fcntl_getfd(fd: BorrowedFd<'_>) -> io::Result<FdFlags> { |
335 | #[cfg (target_pointer_width = "32" )] |
336 | unsafe { |
337 | ret_c_uint(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETFD))) |
338 | .map(FdFlags::from_bits_retain) |
339 | } |
340 | #[cfg (target_pointer_width = "64" )] |
341 | unsafe { |
342 | ret_c_uint(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETFD))) |
343 | .map(op:FdFlags::from_bits_retain) |
344 | } |
345 | } |
346 | |
347 | #[inline ] |
348 | pub(crate) fn fcntl_setfd(fd: BorrowedFd<'_>, flags: FdFlags) -> io::Result<()> { |
349 | #[cfg (target_pointer_width = "32" )] |
350 | unsafe { |
351 | ret(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_SETFD), flags)) |
352 | } |
353 | #[cfg (target_pointer_width = "64" )] |
354 | unsafe { |
355 | ret(raw:syscall_readonly!(__NR_fcntl, fd, c_uint(F_SETFD), flags)) |
356 | } |
357 | } |
358 | |
359 | #[inline ] |
360 | pub(crate) fn fcntl_dupfd_cloexec(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd> { |
361 | #[cfg (target_pointer_width = "32" )] |
362 | unsafe { |
363 | ret_owned_fd(syscall_readonly!( |
364 | __NR_fcntl64, |
365 | fd, |
366 | c_uint(F_DUPFD_CLOEXEC), |
367 | raw_fd(min) |
368 | )) |
369 | } |
370 | #[cfg (target_pointer_width = "64" )] |
371 | unsafe { |
372 | ret_owned_fd(raw:syscall_readonly!( |
373 | __NR_fcntl, |
374 | fd, |
375 | c_uint(F_DUPFD_CLOEXEC), |
376 | raw_fd(min) |
377 | )) |
378 | } |
379 | } |
380 | |