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