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,
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#[cfg(feature = "try_close")]
245#[inline]
246pub(crate) unsafe fn try_close(fd: RawFd) -> io::Result<()> {
247    ret(syscall_readonly!(__NR_close, raw_fd(fd)))
248}
249
250#[inline]
251pub(crate) unsafe fn ioctl(
252    fd: BorrowedFd<'_>,
253    request: RawOpcode,
254    arg: *mut c::c_void,
255) -> io::Result<IoctlOutput> {
256    ret_c_int(syscall!(__NR_ioctl, fd, c_uint(request), arg))
257}
258
259#[inline]
260pub(crate) unsafe fn ioctl_readonly(
261    fd: BorrowedFd<'_>,
262    request: RawOpcode,
263    arg: *mut c::c_void,
264) -> io::Result<IoctlOutput> {
265    ret_c_int(syscall_readonly!(__NR_ioctl, fd, c_uint(request), arg))
266}
267
268#[cfg(all(feature = "fs", feature = "net"))]
269pub(crate) fn is_read_write(fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> {
270    let (mut read, mut write) = crate::fs::fd::_is_file_read_write(fd)?;
271    let mut not_socket = false;
272    if read {
273        // Do a `recv` with `PEEK` and `DONTWAIT` for 1 byte. A 0 indicates
274        // the read side is shut down; an `EWOULDBLOCK` indicates the read
275        // side is still open.
276        let mut buf = [core::mem::MaybeUninit::<u8>::uninit()];
277        match unsafe {
278            crate::backend::net::syscalls::recv(
279                fd,
280                buf.as_mut_ptr().cast::<u8>(),
281                1,
282                RecvFlags::PEEK | RecvFlags::DONTWAIT,
283            )
284        } {
285            Ok(0) => read = false,
286            Err(err) => {
287                #[allow(unreachable_patterns)] // `EAGAIN` may equal `EWOULDBLOCK`
288                match err {
289                    io::Errno::AGAIN | io::Errno::WOULDBLOCK => (),
290                    io::Errno::NOTSOCK => not_socket = true,
291                    _ => return Err(err),
292                }
293            }
294            Ok(_) => (),
295        }
296    }
297    if write && !not_socket {
298        // Do a `send` with `DONTWAIT` for 0 bytes. An `EPIPE` indicates
299        // the write side is shut down.
300        #[allow(unreachable_patterns)] // `EAGAIN` equals `EWOULDBLOCK`
301        match crate::backend::net::syscalls::send(fd, &[], SendFlags::DONTWAIT) {
302            Err(io::Errno::AGAIN | io::Errno::WOULDBLOCK | io::Errno::NOTSOCK) => (),
303            Err(io::Errno::PIPE) => write = false,
304            Err(err) => return Err(err),
305            Ok(_) => (),
306        }
307    }
308    Ok((read, write))
309}
310
311#[inline]
312pub(crate) fn dup(fd: BorrowedFd<'_>) -> io::Result<OwnedFd> {
313    unsafe { ret_owned_fd(syscall_readonly!(__NR_dup, fd)) }
314}
315
316#[allow(clippy::needless_pass_by_ref_mut)]
317#[inline]
318pub(crate) fn dup2(fd: BorrowedFd<'_>, new: &mut OwnedFd) -> io::Result<()> {
319    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
320    {
321        // We don't need to worry about the difference between `dup2` and
322        // `dup3` when the file descriptors are equal because we have an
323        // `&mut OwnedFd` which means `fd` doesn't alias it.
324        dup3(fd, new, DupFlags::empty())
325    }
326
327    #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
328    unsafe {
329        ret_discarded_fd(syscall_readonly!(__NR_dup2, fd, new.as_fd()))
330    }
331}
332
333#[allow(clippy::needless_pass_by_ref_mut)]
334#[inline]
335pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, flags: DupFlags) -> io::Result<()> {
336    unsafe { ret_discarded_fd(syscall_readonly!(__NR_dup3, fd, new.as_fd(), flags)) }
337}
338
339#[inline]
340pub(crate) fn fcntl_getfd(fd: BorrowedFd<'_>) -> io::Result<FdFlags> {
341    #[cfg(target_pointer_width = "32")]
342    unsafe {
343        ret_c_uint(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETFD)))
344            .map(FdFlags::from_bits_retain)
345    }
346    #[cfg(target_pointer_width = "64")]
347    unsafe {
348        ret_c_uint(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETFD)))
349            .map(FdFlags::from_bits_retain)
350    }
351}
352
353#[inline]
354pub(crate) fn fcntl_setfd(fd: BorrowedFd<'_>, flags: FdFlags) -> io::Result<()> {
355    #[cfg(target_pointer_width = "32")]
356    unsafe {
357        ret(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_SETFD), flags))
358    }
359    #[cfg(target_pointer_width = "64")]
360    unsafe {
361        ret(syscall_readonly!(__NR_fcntl, fd, c_uint(F_SETFD), flags))
362    }
363}
364
365#[inline]
366pub(crate) fn fcntl_dupfd_cloexec(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd> {
367    #[cfg(target_pointer_width = "32")]
368    unsafe {
369        ret_owned_fd(syscall_readonly!(
370            __NR_fcntl64,
371            fd,
372            c_uint(F_DUPFD_CLOEXEC),
373            raw_fd(min)
374        ))
375    }
376    #[cfg(target_pointer_width = "64")]
377    unsafe {
378        ret_owned_fd(syscall_readonly!(
379            __NR_fcntl,
380            fd,
381            c_uint(F_DUPFD_CLOEXEC),
382            raw_fd(min)
383        ))
384    }
385}