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
9use super::super::c;
10#[cfg(target_pointer_width = "64")]
11use super::super::conv::loff_t_from_u64;
12use 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,
15};
16#[cfg(target_pointer_width = "32")]
17use super::super::conv::{hi, lo};
18use crate::fd::{AsFd, BorrowedFd, OwnedFd, RawFd};
19#[cfg(linux_kernel)]
20use crate::io::SpliceFlags;
21use crate::io::{
22 self, epoll, DupFlags, EventfdFlags, FdFlags, IoSlice, IoSliceMut, IoSliceRaw, PipeFlags,
23 PollFd, ReadWriteFlags,
24};
25#[cfg(all(feature = "fs", feature = "net"))]
26use crate::net::{RecvFlags, SendFlags};
27use core::cmp;
28use core::mem::MaybeUninit;
29#[cfg(target_os = "espidf")]
30use linux_raw_sys::general::F_DUPFD;
31use linux_raw_sys::general::{
32 epoll_event, EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD, F_DUPFD_CLOEXEC, F_GETFD, F_SETFD,
33 UIO_MAXIOV,
34};
35use linux_raw_sys::ioctl::{
36 BLKPBSZGET, BLKSSZGET, EXT4_IOC_RESIZE_FS, FICLONE, FIONBIO, FIONREAD, TIOCEXCL, TIOCNXCL,
37};
38#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
39use {
40 super::super::conv::{opt_ref, size_of},
41 linux_raw_sys::general::{__kernel_timespec, kernel_sigset_t},
42};
43
44#[inline]
45pub(crate) fn read(fd: BorrowedFd<'_>, buf: &mut [u8]) -> io::Result<usize> {
46 let (buf_addr_mut: ArgReg<'_, A1>, buf_len: ArgReg<'_, A2>) = slice_mut(buf);
47
48 unsafe { ret_usize(raw:syscall!(__NR_read, fd, buf_addr_mut, buf_len)) }
49}
50
51#[inline]
52pub(crate) fn pread(fd: BorrowedFd<'_>, buf: &mut [u8], pos: u64) -> io::Result<usize> {
53 let (buf_addr_mut, buf_len) = slice_mut(buf);
54
55 // <https://github.com/torvalds/linux/blob/fcadab740480e0e0e9fa9bd272acd409884d431a/arch/arm64/kernel/sys32.c#L75>
56 #[cfg(all(
57 target_pointer_width = "32",
58 any(target_arch = "arm", target_arch = "mips", target_arch = "power"),
59 ))]
60 unsafe {
61 ret_usize(syscall!(
62 __NR_pread64,
63 fd,
64 buf_addr_mut,
65 buf_len,
66 zero(),
67 hi(pos),
68 lo(pos)
69 ))
70 }
71 #[cfg(all(
72 target_pointer_width = "32",
73 not(any(target_arch = "arm", target_arch = "mips", target_arch = "power")),
74 ))]
75 unsafe {
76 ret_usize(syscall!(
77 __NR_pread64,
78 fd,
79 buf_addr_mut,
80 buf_len,
81 hi(pos),
82 lo(pos)
83 ))
84 }
85 #[cfg(target_pointer_width = "64")]
86 unsafe {
87 ret_usize(syscall!(
88 __NR_pread64,
89 fd,
90 buf_addr_mut,
91 buf_len,
92 loff_t_from_u64(pos)
93 ))
94 }
95}
96
97#[inline]
98pub(crate) fn readv(fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
99 let (bufs_addr: ArgReg<'_, A1>, bufs_len: ArgReg<'_, A2>) = slice(&bufs[..cmp::min(v1:bufs.len(), v2:max_iov())]);
100
101 unsafe { ret_usize(raw:syscall!(__NR_readv, fd, bufs_addr, bufs_len)) }
102}
103
104#[inline]
105pub(crate) fn preadv(
106 fd: BorrowedFd<'_>,
107 bufs: &mut [IoSliceMut<'_>],
108 pos: u64,
109) -> io::Result<usize> {
110 let (bufs_addr: ArgReg<'_, A1>, bufs_len: ArgReg<'_, A2>) = slice(&bufs[..cmp::min(v1:bufs.len(), v2:max_iov())]);
111
112 // Unlike the plain "p" functions, the "pv" functions pass their offset in
113 // an endian-independent way, and always in two registers.
114 unsafe {
115 ret_usize(raw:syscall!(
116 __NR_preadv,
117 fd,
118 bufs_addr,
119 bufs_len,
120 pass_usize(pos as usize),
121 pass_usize((pos >> 32) as usize)
122 ))
123 }
124}
125
126#[inline]
127pub(crate) fn preadv2(
128 fd: BorrowedFd<'_>,
129 bufs: &mut [IoSliceMut<'_>],
130 pos: u64,
131 flags: ReadWriteFlags,
132) -> io::Result<usize> {
133 let (bufs_addr: ArgReg<'_, A1>, bufs_len: ArgReg<'_, A2>) = slice(&bufs[..cmp::min(v1:bufs.len(), v2:max_iov())]);
134
135 // Unlike the plain "p" functions, the "pv" functions pass their offset in
136 // an endian-independent way, and always in two registers.
137 unsafe {
138 ret_usize(raw:syscall!(
139 __NR_preadv2,
140 fd,
141 bufs_addr,
142 bufs_len,
143 pass_usize(pos as usize),
144 pass_usize((pos >> 32) as usize),
145 flags
146 ))
147 }
148}
149
150#[inline]
151pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize> {
152 let (buf_addr: ArgReg<'_, A1>, buf_len: ArgReg<'_, A2>) = slice(buf);
153
154 unsafe { ret_usize(raw:syscall_readonly!(__NR_write, fd, buf_addr, buf_len)) }
155}
156
157#[inline]
158pub(crate) fn pwrite(fd: BorrowedFd<'_>, buf: &[u8], pos: u64) -> io::Result<usize> {
159 let (buf_addr, buf_len) = slice(buf);
160
161 // <https://github.com/torvalds/linux/blob/fcadab740480e0e0e9fa9bd272acd409884d431a/arch/arm64/kernel/sys32.c#L81-L83>
162 #[cfg(all(
163 target_pointer_width = "32",
164 any(target_arch = "arm", target_arch = "mips", target_arch = "power"),
165 ))]
166 unsafe {
167 ret_usize(syscall_readonly!(
168 __NR_pwrite64,
169 fd,
170 buf_addr,
171 buf_len,
172 zero(),
173 hi(pos),
174 lo(pos)
175 ))
176 }
177 #[cfg(all(
178 target_pointer_width = "32",
179 not(any(target_arch = "arm", target_arch = "mips", target_arch = "power")),
180 ))]
181 unsafe {
182 ret_usize(syscall_readonly!(
183 __NR_pwrite64,
184 fd,
185 buf_addr,
186 buf_len,
187 hi(pos),
188 lo(pos)
189 ))
190 }
191 #[cfg(target_pointer_width = "64")]
192 unsafe {
193 ret_usize(syscall_readonly!(
194 __NR_pwrite64,
195 fd,
196 buf_addr,
197 buf_len,
198 loff_t_from_u64(pos)
199 ))
200 }
201}
202
203#[inline]
204pub(crate) fn writev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
205 let (bufs_addr: ArgReg<'_, A1>, bufs_len: ArgReg<'_, A2>) = slice(&bufs[..cmp::min(v1:bufs.len(), v2:max_iov())]);
206
207 unsafe { ret_usize(raw:syscall_readonly!(__NR_writev, fd, bufs_addr, bufs_len)) }
208}
209
210#[inline]
211pub(crate) fn pwritev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>], pos: u64) -> io::Result<usize> {
212 let (bufs_addr: ArgReg<'_, A1>, bufs_len: ArgReg<'_, A2>) = slice(&bufs[..cmp::min(v1:bufs.len(), v2:max_iov())]);
213
214 // Unlike the plain "p" functions, the "pv" functions pass their offset in
215 // an endian-independent way, and always in two registers.
216 unsafe {
217 ret_usize(raw:syscall_readonly!(
218 __NR_pwritev,
219 fd,
220 bufs_addr,
221 bufs_len,
222 pass_usize(pos as usize),
223 pass_usize((pos >> 32) as usize)
224 ))
225 }
226}
227
228#[inline]
229pub(crate) fn pwritev2(
230 fd: BorrowedFd<'_>,
231 bufs: &[IoSlice<'_>],
232 pos: u64,
233 flags: ReadWriteFlags,
234) -> io::Result<usize> {
235 let (bufs_addr: ArgReg<'_, A1>, bufs_len: ArgReg<'_, A2>) = slice(&bufs[..cmp::min(v1:bufs.len(), v2:max_iov())]);
236
237 // Unlike the plain "p" functions, the "pv" functions pass their offset in
238 // an endian-independent way, and always in two registers.
239 unsafe {
240 ret_usize(raw:syscall_readonly!(
241 __NR_pwritev2,
242 fd,
243 bufs_addr,
244 bufs_len,
245 pass_usize(pos as usize),
246 pass_usize((pos >> 32) as usize),
247 flags
248 ))
249 }
250}
251
252/// The maximum number of buffers that can be passed into a vectored I/O system
253/// call on the current platform.
254const fn max_iov() -> usize {
255 UIO_MAXIOV as usize
256}
257
258#[inline]
259pub(crate) unsafe fn close(fd: RawFd) {
260 // See the documentation for [`io::close`] for why errors are ignored.
261 syscall_readonly!(__NR_close, raw_fd(fd)).decode_void();
262}
263
264#[inline]
265pub(crate) fn eventfd(initval: u32, flags: EventfdFlags) -> io::Result<OwnedFd> {
266 unsafe { ret_owned_fd(raw:syscall_readonly!(__NR_eventfd2, c_uint(initval), flags)) }
267}
268
269#[inline]
270pub(crate) fn ioctl_fionread(fd: BorrowedFd<'_>) -> io::Result<u64> {
271 unsafe {
272 let mut result: MaybeUninit = MaybeUninit::<c::c_int>::uninit();
273 ret(raw:syscall!(__NR_ioctl, fd, c_uint(FIONREAD), &mut result))?;
274 Ok(result.assume_init() as u64)
275 }
276}
277
278#[inline]
279pub(crate) fn ioctl_fionbio(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
280 unsafe {
281 let data: i32 = c::c_int::from(value);
282 ret(raw:syscall_readonly!(
283 __NR_ioctl,
284 fd,
285 c_uint(FIONBIO),
286 by_ref(&data)
287 ))
288 }
289}
290
291#[inline]
292pub(crate) fn ioctl_tiocexcl(fd: BorrowedFd<'_>) -> io::Result<()> {
293 unsafe { ret(raw:syscall_readonly!(__NR_ioctl, fd, c_uint(TIOCEXCL))) }
294}
295
296#[inline]
297pub(crate) fn ioctl_tiocnxcl(fd: BorrowedFd<'_>) -> io::Result<()> {
298 unsafe { ret(raw:syscall_readonly!(__NR_ioctl, fd, c_uint(TIOCNXCL))) }
299}
300
301#[inline]
302pub(crate) fn ioctl_blksszget(fd: BorrowedFd) -> io::Result<u32> {
303 let mut result: MaybeUninit = MaybeUninit::<c::c_uint>::uninit();
304 unsafe {
305 ret(raw:syscall!(__NR_ioctl, fd, c_uint(BLKSSZGET), &mut result))?;
306 Ok(result.assume_init() as u32)
307 }
308}
309
310#[inline]
311pub(crate) fn ioctl_blkpbszget(fd: BorrowedFd) -> io::Result<u32> {
312 let mut result: MaybeUninit = MaybeUninit::<c::c_uint>::uninit();
313 unsafe {
314 ret(raw:syscall!(__NR_ioctl, fd, c_uint(BLKPBSZGET), &mut result))?;
315 Ok(result.assume_init() as u32)
316 }
317}
318
319#[inline]
320pub(crate) fn ioctl_ficlone(fd: BorrowedFd<'_>, src_fd: BorrowedFd<'_>) -> io::Result<()> {
321 unsafe { ret(raw:syscall_readonly!(__NR_ioctl, fd, c_uint(FICLONE), src_fd)) }
322}
323
324#[inline]
325pub(crate) fn ext4_ioc_resize_fs(fd: BorrowedFd<'_>, blocks: u64) -> io::Result<()> {
326 unsafe {
327 ret(raw:syscall_readonly!(
328 __NR_ioctl,
329 fd,
330 c_uint(EXT4_IOC_RESIZE_FS),
331 by_ref(&blocks)
332 ))
333 }
334}
335
336#[cfg(all(feature = "fs", feature = "net"))]
337pub(crate) fn is_read_write(fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> {
338 let (mut read, mut write) = crate::fs::fd::_is_file_read_write(fd)?;
339 let mut not_socket = false;
340 if read {
341 // Do a `recv` with `PEEK` and `DONTWAIT` for 1 byte. A 0 indicates
342 // the read side is shut down; an `EWOULDBLOCK` indicates the read
343 // side is still open.
344 //
345 // TODO: This code would benefit from having a better way to read into
346 // uninitialized memory.
347 let mut buf = [0];
348 match super::super::net::syscalls::recv(fd, &mut buf, RecvFlags::PEEK | RecvFlags::DONTWAIT)
349 {
350 Ok(0) => read = false,
351 Err(err) => {
352 #[allow(unreachable_patterns)] // `EAGAIN` may equal `EWOULDBLOCK`
353 match err {
354 io::Errno::AGAIN | io::Errno::WOULDBLOCK => (),
355 io::Errno::NOTSOCK => not_socket = true,
356 _ => return Err(err),
357 }
358 }
359 Ok(_) => (),
360 }
361 }
362 if write && !not_socket {
363 // Do a `send` with `DONTWAIT` for 0 bytes. An `EPIPE` indicates
364 // the write side is shut down.
365 #[allow(unreachable_patterns)] // `EAGAIN` equals `EWOULDBLOCK`
366 match super::super::net::syscalls::send(fd, &[], SendFlags::DONTWAIT) {
367 // TODO or-patterns when we don't need 1.51
368 Err(io::Errno::AGAIN) => (),
369 Err(io::Errno::WOULDBLOCK) => (),
370 Err(io::Errno::NOTSOCK) => (),
371 Err(io::Errno::PIPE) => write = false,
372 Err(err) => return Err(err),
373 Ok(_) => (),
374 }
375 }
376 Ok((read, write))
377}
378
379#[inline]
380pub(crate) fn dup(fd: BorrowedFd<'_>) -> io::Result<OwnedFd> {
381 unsafe { ret_owned_fd(raw:syscall_readonly!(__NR_dup, fd)) }
382}
383
384#[inline]
385pub(crate) fn dup2(fd: BorrowedFd<'_>, new: &mut OwnedFd) -> io::Result<()> {
386 #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
387 {
388 // We don't need to worry about the difference between `dup2` and
389 // `dup3` when the file descriptors are equal because we have an
390 // `&mut OwnedFd` which means `fd` doesn't alias it.
391 dup3(fd, new, DupFlags::empty())
392 }
393
394 #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
395 unsafe {
396 ret_discarded_fd(raw:syscall_readonly!(__NR_dup2, fd, new.as_fd()))
397 }
398}
399
400#[inline]
401pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, flags: DupFlags) -> io::Result<()> {
402 unsafe { ret_discarded_fd(raw:syscall_readonly!(__NR_dup3, fd, new.as_fd(), flags)) }
403}
404
405#[inline]
406pub(crate) fn fcntl_getfd(fd: BorrowedFd<'_>) -> io::Result<FdFlags> {
407 #[cfg(target_pointer_width = "32")]
408 unsafe {
409 ret_c_uint(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETFD)))
410 .map(FdFlags::from_bits_truncate)
411 }
412 #[cfg(target_pointer_width = "64")]
413 unsafe {
414 ret_c_uint(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETFD)))
415 .map(op:FdFlags::from_bits_truncate)
416 }
417}
418
419#[inline]
420pub(crate) fn fcntl_setfd(fd: BorrowedFd<'_>, flags: FdFlags) -> io::Result<()> {
421 #[cfg(target_pointer_width = "32")]
422 unsafe {
423 ret(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_SETFD), flags))
424 }
425 #[cfg(target_pointer_width = "64")]
426 unsafe {
427 ret(raw:syscall_readonly!(__NR_fcntl, fd, c_uint(F_SETFD), flags))
428 }
429}
430
431#[cfg(target_os = "espidf")]
432#[inline]
433pub(crate) fn fcntl_dupfd(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd> {
434 #[cfg(target_pointer_width = "32")]
435 unsafe {
436 ret_owned_fd(syscall_readonly!(
437 __NR_fcntl64,
438 fd,
439 c_uint(F_DUPFD),
440 raw_fd(min)
441 ))
442 }
443 #[cfg(target_pointer_width = "64")]
444 unsafe {
445 ret_owned_fd(syscall_readonly!(
446 __NR_fcntl,
447 fd,
448 c_uint(F_DUPFD),
449 raw_fd(min)
450 ))
451 }
452}
453
454#[inline]
455pub(crate) fn fcntl_dupfd_cloexec(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd> {
456 #[cfg(target_pointer_width = "32")]
457 unsafe {
458 ret_owned_fd(syscall_readonly!(
459 __NR_fcntl64,
460 fd,
461 c_uint(F_DUPFD_CLOEXEC),
462 raw_fd(min)
463 ))
464 }
465 #[cfg(target_pointer_width = "64")]
466 unsafe {
467 ret_owned_fd(raw:syscall_readonly!(
468 __NR_fcntl,
469 fd,
470 c_uint(F_DUPFD_CLOEXEC),
471 raw_fd(min)
472 ))
473 }
474}
475
476#[inline]
477pub(crate) fn pipe_with(flags: PipeFlags) -> io::Result<(OwnedFd, OwnedFd)> {
478 unsafe {
479 let mut result: MaybeUninit<[OwnedFd; 2]> = MaybeUninit::<[OwnedFd; 2]>::uninit();
480 ret(raw:syscall!(__NR_pipe2, &mut result, flags))?;
481 let [p0: OwnedFd, p1: OwnedFd] = result.assume_init();
482 Ok((p0, p1))
483 }
484}
485
486#[inline]
487pub(crate) fn pipe() -> io::Result<(OwnedFd, OwnedFd)> {
488 // aarch64 and risc64 omit `__NR_pipe`. On mips, `__NR_pipe` uses a special
489 // calling convention, but using it is not worth complicating our syscall
490 // wrapping infrastructure at this time.
491 #[cfg(any(
492 target_arch = "aarch64",
493 target_arch = "mips",
494 target_arch = "mips64",
495 target_arch = "riscv64",
496 ))]
497 {
498 pipe_with(PipeFlags::empty())
499 }
500 #[cfg(not(any(
501 target_arch = "aarch64",
502 target_arch = "mips",
503 target_arch = "mips64",
504 target_arch = "riscv64",
505 )))]
506 unsafe {
507 let mut result = MaybeUninit::<[OwnedFd; 2]>::uninit();
508 ret(syscall!(__NR_pipe, &mut result))?;
509 let [p0, p1] = result.assume_init();
510 Ok((p0, p1))
511 }
512}
513
514#[inline]
515pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: c::c_int) -> io::Result<usize> {
516 let (fds_addr_mut, fds_len) = slice_mut(fds);
517
518 #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
519 unsafe {
520 let timeout = if timeout >= 0 {
521 Some(__kernel_timespec {
522 tv_sec: (timeout as i64) / 1000,
523 tv_nsec: (timeout as i64) % 1000 * 1_000_000,
524 })
525 } else {
526 None
527 };
528 ret_usize(syscall!(
529 __NR_ppoll,
530 fds_addr_mut,
531 fds_len,
532 opt_ref(timeout.as_ref()),
533 zero(),
534 size_of::<kernel_sigset_t, _>()
535 ))
536 }
537 #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
538 unsafe {
539 ret_usize(syscall!(__NR_poll, fds_addr_mut, fds_len, c_int(timeout)))
540 }
541}
542
543#[inline]
544pub(crate) fn epoll_create(flags: epoll::CreateFlags) -> io::Result<OwnedFd> {
545 unsafe { ret_owned_fd(raw:syscall_readonly!(__NR_epoll_create1, flags)) }
546}
547
548#[inline]
549pub(crate) unsafe fn epoll_add(
550 epfd: BorrowedFd<'_>,
551 fd: c::c_int,
552 event: &epoll_event,
553) -> io::Result<()> {
554 ret(raw:syscall_readonly!(
555 __NR_epoll_ctl,
556 epfd,
557 c_uint(EPOLL_CTL_ADD),
558 raw_fd(fd),
559 by_ref(event)
560 ))
561}
562
563#[inline]
564pub(crate) unsafe fn epoll_mod(
565 epfd: BorrowedFd<'_>,
566 fd: c::c_int,
567 event: &epoll_event,
568) -> io::Result<()> {
569 ret(raw:syscall_readonly!(
570 __NR_epoll_ctl,
571 epfd,
572 c_uint(EPOLL_CTL_MOD),
573 raw_fd(fd),
574 by_ref(event)
575 ))
576}
577
578#[inline]
579pub(crate) unsafe fn epoll_del(epfd: BorrowedFd<'_>, fd: c::c_int) -> io::Result<()> {
580 ret(raw:syscall_readonly!(
581 __NR_epoll_ctl,
582 epfd,
583 c_uint(EPOLL_CTL_DEL),
584 raw_fd(fd),
585 zero()
586 ))
587}
588
589#[inline]
590pub(crate) fn epoll_wait(
591 epfd: BorrowedFd<'_>,
592 events: *mut epoll_event,
593 num_events: usize,
594 timeout: c::c_int,
595) -> io::Result<usize> {
596 #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
597 unsafe {
598 ret_usize(raw:syscall!(
599 __NR_epoll_wait,
600 epfd,
601 events,
602 pass_usize(num_events),
603 c_int(timeout)
604 ))
605 }
606 #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
607 unsafe {
608 ret_usize(syscall!(
609 __NR_epoll_pwait,
610 epfd,
611 events,
612 pass_usize(num_events),
613 c_int(timeout),
614 zero()
615 ))
616 }
617}
618
619#[cfg(linux_kernel)]
620#[inline]
621pub fn splice(
622 fd_in: BorrowedFd,
623 off_in: Option<&mut u64>,
624 fd_out: BorrowedFd,
625 off_out: Option<&mut u64>,
626 len: usize,
627 flags: SpliceFlags,
628) -> io::Result<usize> {
629 unsafe {
630 ret_usize(raw:syscall!(
631 __NR_splice,
632 fd_in,
633 opt_mut(off_in),
634 fd_out,
635 opt_mut(off_out),
636 pass_usize(len),
637 c_uint(flags.bits())
638 ))
639 }
640}
641
642#[cfg(linux_kernel)]
643#[inline]
644pub unsafe fn vmsplice(
645 fd: BorrowedFd,
646 bufs: &[IoSliceRaw],
647 flags: SpliceFlags,
648) -> io::Result<usize> {
649 let (bufs_addr: ArgReg<'_, A1>, bufs_len: ArgReg<'_, A2>) = slice(&bufs[..cmp::min(v1:bufs.len(), v2:max_iov())]);
650 ret_usize(raw:syscall!(
651 __NR_vmsplice,
652 fd,
653 bufs_addr,
654 bufs_len,
655 c_uint(flags.bits())
656 ))
657}
658