rustix/backend/linux_raw/io/
syscalls.rs

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