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