1 //! linux_raw syscalls supporting `rustix::io`.
5 //! See the `rustix::backend` module documentation for details.
7 #![allow(clippy::undocumented_unsafe_blocks)]
10 #[cfg(target_pointer_width = "64")]
11 use super::super::conv
::loff_t_from_u64
;
12 use super::super::conv
::{
13 by_ref
, c_int
, c_uint
, opt_mut
, pass_usize
, raw_fd
, ret
, ret_c_uint
, ret_discarded_fd
,
14 ret_owned_fd
, ret_usize
, slice
, slice_mut
, zero
,
16 #[cfg(target_pointer_width = "32")]
17 use super::super::conv
::{hi, lo}
;
18 use crate::fd
::{AsFd, BorrowedFd, OwnedFd, RawFd}
;
19 #[cfg(any(target_os = "android", target_os = "linux"))]
20 use crate::io
::SpliceFlags
;
22 self, epoll
, DupFlags
, EventfdFlags
, FdFlags
, IoSlice
, IoSliceMut
, IoSliceRaw
, PipeFlags
,
23 PollFd
, ReadWriteFlags
,
25 #[cfg(all(feature = "fs", feature = "net"))]
26 use crate::net
::{RecvFlags, SendFlags}
;
28 use core
::mem
::MaybeUninit
;
29 #[cfg(target_os = "espidf")]
30 use linux_raw_sys
::general
::F_DUPFD
;
31 use linux_raw_sys
::general
::{
32 epoll_event
, EPOLL_CTL_ADD
, EPOLL_CTL_DEL
, EPOLL_CTL_MOD
, F_DUPFD_CLOEXEC
, F_GETFD
, F_SETFD
,
35 use linux_raw_sys
::ioctl
::{BLKPBSZGET, BLKSSZGET, FICLONE, FIONBIO, FIONREAD, TIOCEXCL, TIOCNXCL}
;
36 #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
38 super::super::conv
::{opt_ref, size_of}
,
39 linux_raw_sys
::general
::{__kernel_timespec, sigset_t}
,
43 pub(crate) fn read(fd
: BorrowedFd
<'_
>, buf
: &mut [u8]) -> io
::Result
<usize> {
44 let (buf_addr_mut
, buf_len
) = slice_mut(buf
);
46 unsafe { ret_usize(syscall!(__NR_read, fd, buf_addr_mut, buf_len)) }
50 pub(crate) fn pread(fd
: BorrowedFd
<'_
>, buf
: &mut [u8], pos
: u64) -> io
::Result
<usize> {
51 let (buf_addr_mut
, buf_len
) = slice_mut(buf
);
53 // <https://github.com/torvalds/linux/blob/fcadab740480e0e0e9fa9bd272acd409884d431a/arch/arm64/kernel/sys32.c#L75>
55 target_pointer_width
= "32",
56 any(target_arch
= "arm", target_arch
= "mips", target_arch
= "power"),
70 target_pointer_width
= "32",
71 not(any(target_arch
= "arm", target_arch
= "mips", target_arch
= "power")),
83 #[cfg(target_pointer_width = "64")]
96 pub(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())]);
99 unsafe { ret_usize(syscall!(__NR_readv, fd, bufs_addr, bufs_len)) }
103 pub(crate) fn preadv(
105 bufs
: &mut [IoSliceMut
<'_
>],
107 ) -> io
::Result
<usize> {
108 let (bufs_addr
, bufs_len
) = slice(&bufs
[..cmp
::min(bufs
.len(), max_iov())]);
110 #[cfg(target_pointer_width = "32")]
121 #[cfg(target_pointer_width = "64")]
134 pub(crate) fn preadv2(
136 bufs
: &mut [IoSliceMut
<'_
>],
138 flags
: ReadWriteFlags
,
139 ) -> io
::Result
<usize> {
140 let (bufs_addr
, bufs_len
) = slice(&bufs
[..cmp
::min(bufs
.len(), max_iov())]);
142 #[cfg(target_pointer_width = "32")]
154 #[cfg(target_pointer_width = "64")]
161 loff_t_from_u64(pos
),
168 pub(crate) fn write(fd
: BorrowedFd
<'_
>, buf
: &[u8]) -> io
::Result
<usize> {
169 let (buf_addr
, buf_len
) = slice(buf
);
171 unsafe { ret_usize(syscall_readonly!(__NR_write, fd, buf_addr, buf_len)) }
175 pub(crate) fn pwrite(fd
: BorrowedFd
<'_
>, buf
: &[u8], pos
: u64) -> io
::Result
<usize> {
176 let (buf_addr
, buf_len
) = slice(buf
);
178 // <https://github.com/torvalds/linux/blob/fcadab740480e0e0e9fa9bd272acd409884d431a/arch/arm64/kernel/sys32.c#L81-L83>
180 target_pointer_width
= "32",
181 any(target_arch
= "arm", target_arch
= "mips", target_arch
= "power"),
184 ret_usize(syscall_readonly
!(
195 target_pointer_width
= "32",
196 not(any(target_arch
= "arm", target_arch
= "mips", target_arch
= "power")),
199 ret_usize(syscall_readonly
!(
208 #[cfg(target_pointer_width = "64")]
210 ret_usize(syscall_readonly
!(
221 pub(crate) fn writev(fd
: BorrowedFd
<'_
>, bufs
: &[IoSlice
<'_
>]) -> io
::Result
<usize> {
222 let (bufs_addr
, bufs_len
) = slice(&bufs
[..cmp
::min(bufs
.len(), max_iov())]);
224 unsafe { ret_usize(syscall_readonly!(__NR_writev, fd, bufs_addr, bufs_len)) }
228 pub(crate) fn pwritev(fd
: BorrowedFd
<'_
>, bufs
: &[IoSlice
<'_
>], pos
: u64) -> io
::Result
<usize> {
229 let (bufs_addr
, bufs_len
) = slice(&bufs
[..cmp
::min(bufs
.len(), max_iov())]);
231 #[cfg(target_pointer_width = "32")]
233 ret_usize(syscall_readonly
!(
242 #[cfg(target_pointer_width = "64")]
244 ret_usize(syscall_readonly
!(
255 pub(crate) fn pwritev2(
257 bufs
: &[IoSlice
<'_
>],
259 flags
: ReadWriteFlags
,
260 ) -> io
::Result
<usize> {
261 let (bufs_addr
, bufs_len
) = slice(&bufs
[..cmp
::min(bufs
.len(), max_iov())]);
263 #[cfg(target_pointer_width = "32")]
265 ret_usize(syscall_readonly
!(
275 #[cfg(target_pointer_width = "64")]
277 ret_usize(syscall_readonly
!(
282 loff_t_from_u64(pos
),
288 /// The maximum number of buffers that can be passed into a vectored I/O system
289 /// call on the current platform.
290 const fn max_iov() -> usize {
295 pub(crate) unsafe fn close(fd
: RawFd
) {
296 // See the documentation for [`io::close`] for why errors are ignored.
297 syscall_readonly
!(__NR_close
, raw_fd(fd
)).decode_void();
301 pub(crate) fn eventfd(initval
: u32, flags
: EventfdFlags
) -> io
::Result
<OwnedFd
> {
302 unsafe { ret_owned_fd(syscall_readonly!(__NR_eventfd2, c_uint(initval), flags)) }
306 pub(crate) fn ioctl_fionread(fd
: BorrowedFd
<'_
>) -> io
::Result
<u64> {
308 let mut result
= MaybeUninit
::<c
::c_int
>::uninit();
309 ret(syscall
!(__NR_ioctl
, fd
, c_uint(FIONREAD
), &mut result
))?
;
310 Ok(result
.assume_init() as u64)
315 pub(crate) fn ioctl_fionbio(fd
: BorrowedFd
<'_
>, value
: bool
) -> io
::Result
<()> {
317 let data
= c
::c_int
::from(value
);
318 ret(syscall_readonly
!(
328 pub(crate) fn ioctl_tiocexcl(fd
: BorrowedFd
<'_
>) -> io
::Result
<()> {
329 unsafe { ret(syscall_readonly!(__NR_ioctl, fd, c_uint(TIOCEXCL))) }
333 pub(crate) fn ioctl_tiocnxcl(fd
: BorrowedFd
<'_
>) -> io
::Result
<()> {
334 unsafe { ret(syscall_readonly!(__NR_ioctl, fd, c_uint(TIOCNXCL))) }
338 pub(crate) fn ioctl_blksszget(fd
: BorrowedFd
) -> io
::Result
<u32> {
339 let mut result
= MaybeUninit
::<c
::c_uint
>::uninit();
341 ret(syscall
!(__NR_ioctl
, fd
, c_uint(BLKSSZGET
), &mut result
))?
;
342 Ok(result
.assume_init() as u32)
347 pub(crate) fn ioctl_blkpbszget(fd
: BorrowedFd
) -> io
::Result
<u32> {
348 let mut result
= MaybeUninit
::<c
::c_uint
>::uninit();
350 ret(syscall
!(__NR_ioctl
, fd
, c_uint(BLKPBSZGET
), &mut result
))?
;
351 Ok(result
.assume_init() as u32)
356 pub(crate) fn ioctl_ficlone(fd
: BorrowedFd
<'_
>, src_fd
: BorrowedFd
<'_
>) -> io
::Result
<()> {
357 unsafe { ret(syscall_readonly!(__NR_ioctl, fd, c_uint(FICLONE), src_fd)) }
360 #[cfg(all(feature = "fs", feature = "net"))]
361 pub(crate) fn is_read_write(fd
: BorrowedFd
<'_
>) -> io
::Result
<(bool
, bool
)> {
362 let (mut read
, mut write
) = crate::fs
::fd
::_is_file_read_write(fd
)?
;
363 let mut not_socket
= false;
365 // Do a `recv` with `PEEK` and `DONTWAIT` for 1 byte. A 0 indicates
366 // the read side is shut down; an `EWOULDBLOCK` indicates the read
367 // side is still open.
369 // TODO: This code would benefit from having a better way to read into
370 // uninitialized memory.
372 match super::super::net
::syscalls
::recv(fd
, &mut buf
, RecvFlags
::PEEK
| RecvFlags
::DONTWAIT
)
374 Ok(0) => read
= false,
376 #[allow(unreachable_patterns)] // `EAGAIN` may equal `EWOULDBLOCK`
378 io
::Errno
::AGAIN
| io
::Errno
::WOULDBLOCK
=> (),
379 io
::Errno
::NOTSOCK
=> not_socket
= true,
380 _
=> return Err(err
),
386 if write
&& !not_socket
{
387 // Do a `send` with `DONTWAIT` for 0 bytes. An `EPIPE` indicates
388 // the write side is shut down.
389 #[allow(unreachable_patterns)] // `EAGAIN` equals `EWOULDBLOCK`
390 match super::super::net
::syscalls
::send(fd
, &[], SendFlags
::DONTWAIT
) {
391 // TODO or-patterns when we don't need 1.51
392 Err(io
::Errno
::AGAIN
) => (),
393 Err(io
::Errno
::WOULDBLOCK
) => (),
394 Err(io
::Errno
::NOTSOCK
) => (),
395 Err(io
::Errno
::PIPE
) => write
= false,
396 Err(err
) => return Err(err
),
404 pub(crate) fn dup(fd
: BorrowedFd
<'_
>) -> io
::Result
<OwnedFd
> {
405 unsafe { ret_owned_fd(syscall_readonly!(__NR_dup, fd)) }
409 pub(crate) fn dup2(fd
: BorrowedFd
<'_
>, new
: &mut OwnedFd
) -> io
::Result
<()> {
410 #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
412 // We don't need to worry about the difference between `dup2` and
413 // `dup3` when the file descriptors are equal because we have an
414 // `&mut OwnedFd` which means `fd` doesn't alias it.
415 dup3(fd
, new
, DupFlags
::empty())
418 #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
420 ret_discarded_fd(syscall_readonly
!(__NR_dup2
, fd
, new
.as_fd()))
425 pub(crate) fn dup3(fd
: BorrowedFd
<'_
>, new
: &mut OwnedFd
, flags
: DupFlags
) -> io
::Result
<()> {
426 unsafe { ret_discarded_fd(syscall_readonly!(__NR_dup3, fd, new.as_fd(), flags)) }
430 pub(crate) fn fcntl_getfd(fd
: BorrowedFd
<'_
>) -> io
::Result
<FdFlags
> {
431 #[cfg(target_pointer_width = "32")]
433 ret_c_uint(syscall_readonly
!(__NR_fcntl64
, fd
, c_uint(F_GETFD
)))
434 .map(FdFlags
::from_bits_truncate
)
436 #[cfg(target_pointer_width = "64")]
438 ret_c_uint(syscall_readonly
!(__NR_fcntl
, fd
, c_uint(F_GETFD
)))
439 .map(FdFlags
::from_bits_truncate
)
444 pub(crate) fn fcntl_setfd(fd
: BorrowedFd
<'_
>, flags
: FdFlags
) -> io
::Result
<()> {
445 #[cfg(target_pointer_width = "32")]
447 ret(syscall_readonly
!(__NR_fcntl64
, fd
, c_uint(F_SETFD
), flags
))
449 #[cfg(target_pointer_width = "64")]
451 ret(syscall_readonly
!(__NR_fcntl
, fd
, c_uint(F_SETFD
), flags
))
455 #[cfg(target_os = "espidf")]
457 pub(crate) fn fcntl_dupfd(fd
: BorrowedFd
<'_
>, min
: RawFd
) -> io
::Result
<OwnedFd
> {
458 #[cfg(target_pointer_width = "32")]
460 ret_owned_fd(syscall_readonly
!(
467 #[cfg(target_pointer_width = "64")]
469 ret_owned_fd(syscall_readonly
!(
479 pub(crate) fn fcntl_dupfd_cloexec(fd
: BorrowedFd
<'_
>, min
: RawFd
) -> io
::Result
<OwnedFd
> {
480 #[cfg(target_pointer_width = "32")]
482 ret_owned_fd(syscall_readonly
!(
485 c_uint(F_DUPFD_CLOEXEC
),
489 #[cfg(target_pointer_width = "64")]
491 ret_owned_fd(syscall_readonly
!(
494 c_uint(F_DUPFD_CLOEXEC
),
501 pub(crate) fn pipe_with(flags
: PipeFlags
) -> io
::Result
<(OwnedFd
, OwnedFd
)> {
503 let mut result
= MaybeUninit
::<[OwnedFd
; 2]>::uninit();
504 ret(syscall
!(__NR_pipe2
, &mut result
, flags
))?
;
505 let [p0
, p1
] = result
.assume_init();
511 pub(crate) fn pipe() -> io
::Result
<(OwnedFd
, OwnedFd
)> {
512 // aarch64 and risc64 omit `__NR_pipe`. On mips, `__NR_pipe` uses a special
513 // calling convention, but using it is not worth complicating our syscall
514 // wrapping infrastructure at this time.
516 target_arch
= "aarch64",
517 target_arch
= "mips",
518 target_arch
= "mips64",
519 target_arch
= "riscv64",
522 pipe_with(PipeFlags
::empty())
525 target_arch
= "aarch64",
526 target_arch
= "mips",
527 target_arch
= "mips64",
528 target_arch
= "riscv64",
531 let mut result
= MaybeUninit
::<[OwnedFd
; 2]>::uninit();
532 ret(syscall
!(__NR_pipe
, &mut result
))?
;
533 let [p0
, p1
] = result
.assume_init();
539 pub(crate) fn poll(fds
: &mut [PollFd
<'_
>], timeout
: c
::c_int
) -> io
::Result
<usize> {
540 let (fds_addr_mut
, fds_len
) = slice_mut(fds
);
542 #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
544 let timeout
= if timeout
>= 0 {
545 Some(__kernel_timespec
{
546 tv_sec
: (timeout
as i64) / 1000,
547 tv_nsec
: (timeout
as i64) % 1000 * 1_000_000,
556 opt_ref(timeout
.as_ref()),
558 size_of
::<sigset_t
, _
>()
561 #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
563 ret_usize(syscall
!(__NR_poll
, fds_addr_mut
, fds_len
, c_int(timeout
)))
568 pub(crate) fn epoll_create(flags
: epoll
::CreateFlags
) -> io
::Result
<OwnedFd
> {
569 unsafe { ret_owned_fd(syscall_readonly!(__NR_epoll_create1, flags)) }
573 pub(crate) unsafe fn epoll_add(
574 epfd
: BorrowedFd
<'_
>,
577 ) -> io
::Result
<()> {
578 ret(syscall_readonly
!(
581 c_uint(EPOLL_CTL_ADD
),
588 pub(crate) unsafe fn epoll_mod(
589 epfd
: BorrowedFd
<'_
>,
592 ) -> io
::Result
<()> {
593 ret(syscall_readonly
!(
596 c_uint(EPOLL_CTL_MOD
),
603 pub(crate) unsafe fn epoll_del(epfd
: BorrowedFd
<'_
>, fd
: c
::c_int
) -> io
::Result
<()> {
604 ret(syscall_readonly
!(
607 c_uint(EPOLL_CTL_DEL
),
614 pub(crate) fn epoll_wait(
615 epfd
: BorrowedFd
<'_
>,
616 events
: *mut epoll_event
,
619 ) -> io
::Result
<usize> {
620 #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
626 pass_usize(num_events
),
630 #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
636 pass_usize(num_events
),
643 #[cfg(any(target_os = "android", target_os = "linux"))]
647 off_in
: Option
<&mut u64>,
649 off_out
: Option
<&mut u64>,
652 ) -> io
::Result
<usize> {
666 #[cfg(any(target_os = "android", target_os = "linux"))]
668 pub unsafe fn vmsplice(
672 ) -> io
::Result
<usize> {
673 let (bufs_addr
, bufs_len
) = slice(&bufs
[..cmp
::min(bufs
.len(), max_iov())]);