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")]
10use 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))]
15use crate::backend::conv::zero;
16use 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")]
21use crate::backend::conv::{hi, lo};
22use crate::backend::{c, MAX_IOV};
23use crate::fd::{AsFd, BorrowedFd, OwnedFd, RawFd};
24use crate::io::{self, DupFlags, FdFlags, IoSlice, IoSliceMut, ReadWriteFlags};
25use crate::ioctl::{IoctlOutput, RawOpcode};
26#[cfg(all(feature = "fs", feature = "net"))]
27use crate::net::{RecvFlags, SendFlags};
28use core::cmp;
29use linux_raw_sys::general::{F_DUPFD_CLOEXEC, F_GETFD, F_SETFD};
30
31#[inline]
32pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: *mut u8, len: usize) -> io::Result<usize> {
33 ret_usize(syscall!(__NR_read, fd, buf, pass_usize(len)))
34}
35
36#[inline]
37pub(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]
84pub(crate) fn readv(fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
85 let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]);
86
87 unsafe { ret_usize(syscall!(__NR_readv, fd, bufs_addr, bufs_len)) }
88}
89
90#[inline]
91pub(crate) fn preadv(
92 fd: BorrowedFd<'_>,
93 bufs: &mut [IoSliceMut<'_>],
94 pos: u64,
95) -> io::Result<usize> {
96 let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(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(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]
113pub(crate) fn preadv2(
114 fd: BorrowedFd<'_>,
115 bufs: &mut [IoSliceMut<'_>],
116 pos: u64,
117 flags: ReadWriteFlags,
118) -> io::Result<usize> {
119 let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(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(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]
137pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize> {
138 let (buf_addr, buf_len) = slice(buf);
139
140 unsafe { ret_usize(syscall_readonly!(__NR_write, fd, buf_addr, buf_len)) }
141}
142
143#[inline]
144pub(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]
190pub(crate) fn writev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
191 let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]);
192
193 unsafe { ret_usize(syscall_readonly!(__NR_writev, fd, bufs_addr, bufs_len)) }
194}
195
196#[inline]
197pub(crate) fn pwritev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>], pos: u64) -> io::Result<usize> {
198 let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(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(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]
215pub(crate) fn pwritev2(
216 fd: BorrowedFd<'_>,
217 bufs: &[IoSlice<'_>],
218 pos: u64,
219 flags: ReadWriteFlags,
220) -> io::Result<usize> {
221 let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(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(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]
239pub(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]
245pub(crate) unsafe fn ioctl(
246 fd: BorrowedFd<'_>,
247 request: RawOpcode,
248 arg: *mut c::c_void,
249) -> io::Result<IoctlOutput> {
250 ret_c_int(syscall!(__NR_ioctl, fd, c_uint(request), arg))
251}
252
253#[inline]
254pub(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(syscall_readonly!(__NR_ioctl, fd, c_uint(request), arg))
260}
261
262#[cfg(all(feature = "fs", feature = "net"))]
263pub(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]
306pub(crate) fn dup(fd: BorrowedFd<'_>) -> io::Result<OwnedFd> {
307 unsafe { ret_owned_fd(syscall_readonly!(__NR_dup, fd)) }
308}
309
310#[allow(clippy::needless_pass_by_ref_mut)]
311#[inline]
312pub(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(syscall_readonly!(__NR_dup2, fd, new.as_fd()))
324 }
325}
326
327#[allow(clippy::needless_pass_by_ref_mut)]
328#[inline]
329pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, flags: DupFlags) -> io::Result<()> {
330 unsafe { ret_discarded_fd(syscall_readonly!(__NR_dup3, fd, new.as_fd(), flags)) }
331}
332
333#[inline]
334pub(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(FdFlags::from_bits_retain)
344 }
345}
346
347#[inline]
348pub(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(syscall_readonly!(__NR_fcntl, fd, c_uint(F_SETFD), flags))
356 }
357}
358
359#[inline]
360pub(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(syscall_readonly!(
373 __NR_fcntl,
374 fd,
375 c_uint(F_DUPFD_CLOEXEC),
376 raw_fd(min)
377 ))
378 }
379}
380