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