• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! libc syscalls supporting `rustix::io`.
2 
3 use super::super::c;
4 #[cfg(any(target_os = "android", target_os = "linux"))]
5 use super::super::conv::syscall_ret_owned_fd;
6 use super::super::conv::{
7     borrowed_fd, ret, ret_c_int, ret_discarded_fd, ret_owned_fd, ret_ssize_t,
8 };
9 use super::super::offset::{libc_pread, libc_pwrite};
10 #[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "solaris")))]
11 use super::super::offset::{libc_preadv, libc_pwritev};
12 #[cfg(all(target_os = "linux", target_env = "gnu"))]
13 use super::super::offset::{libc_preadv2, libc_pwritev2};
14 use crate::fd::{AsFd, BorrowedFd, OwnedFd, RawFd};
15 #[cfg(not(any(target_os = "aix", target_os = "wasi")))]
16 use crate::io::DupFlags;
17 #[cfg(not(any(
18     target_os = "aix",
19     target_os = "haiku",
20     target_os = "ios",
21     target_os = "macos",
22     target_os = "wasi"
23 )))]
24 use crate::io::PipeFlags;
25 use crate::io::{self, FdFlags, IoSlice, IoSliceMut, PollFd};
26 #[cfg(any(target_os = "android", target_os = "linux"))]
27 use crate::io::{EventfdFlags, IoSliceRaw, ReadWriteFlags, SpliceFlags};
28 use core::cmp::min;
29 use core::convert::TryInto;
30 use core::mem::MaybeUninit;
31 #[cfg(any(target_os = "android", target_os = "linux"))]
32 use core::ptr;
33 #[cfg(all(feature = "fs", feature = "net"))]
34 use libc_errno::errno;
35 
read(fd: BorrowedFd<'_>, buf: &mut [u8]) -> io::Result<usize>36 pub(crate) fn read(fd: BorrowedFd<'_>, buf: &mut [u8]) -> io::Result<usize> {
37     let nread = unsafe {
38         ret_ssize_t(c::read(
39             borrowed_fd(fd),
40             buf.as_mut_ptr().cast(),
41             min(buf.len(), READ_LIMIT),
42         ))?
43     };
44     Ok(nread as usize)
45 }
46 
write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize>47 pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize> {
48     let nwritten = unsafe {
49         ret_ssize_t(c::write(
50             borrowed_fd(fd),
51             buf.as_ptr().cast(),
52             min(buf.len(), READ_LIMIT),
53         ))?
54     };
55     Ok(nwritten as usize)
56 }
57 
pread(fd: BorrowedFd<'_>, buf: &mut [u8], offset: u64) -> io::Result<usize>58 pub(crate) fn pread(fd: BorrowedFd<'_>, buf: &mut [u8], offset: u64) -> io::Result<usize> {
59     let len = min(buf.len(), READ_LIMIT);
60 
61     // Silently cast; we'll get `EINVAL` if the value is negative.
62     let offset = offset as i64;
63 
64     let nread = unsafe {
65         ret_ssize_t(libc_pread(
66             borrowed_fd(fd),
67             buf.as_mut_ptr().cast(),
68             len,
69             offset,
70         ))?
71     };
72     Ok(nread as usize)
73 }
74 
pwrite(fd: BorrowedFd<'_>, buf: &[u8], offset: u64) -> io::Result<usize>75 pub(crate) fn pwrite(fd: BorrowedFd<'_>, buf: &[u8], offset: u64) -> io::Result<usize> {
76     let len = min(buf.len(), READ_LIMIT);
77 
78     // Silently cast; we'll get `EINVAL` if the value is negative.
79     let offset = offset as i64;
80 
81     let nwritten = unsafe {
82         ret_ssize_t(libc_pwrite(
83             borrowed_fd(fd),
84             buf.as_ptr().cast(),
85             len,
86             offset,
87         ))?
88     };
89     Ok(nwritten as usize)
90 }
91 
readv(fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut]) -> io::Result<usize>92 pub(crate) fn readv(fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut]) -> io::Result<usize> {
93     let nread = unsafe {
94         ret_ssize_t(c::readv(
95             borrowed_fd(fd),
96             bufs.as_ptr().cast::<c::iovec>(),
97             min(bufs.len(), max_iov()) as c::c_int,
98         ))?
99     };
100     Ok(nread as usize)
101 }
102 
writev(fd: BorrowedFd<'_>, bufs: &[IoSlice]) -> io::Result<usize>103 pub(crate) fn writev(fd: BorrowedFd<'_>, bufs: &[IoSlice]) -> io::Result<usize> {
104     let nwritten = unsafe {
105         ret_ssize_t(c::writev(
106             borrowed_fd(fd),
107             bufs.as_ptr().cast::<c::iovec>(),
108             min(bufs.len(), max_iov()) as c::c_int,
109         ))?
110     };
111     Ok(nwritten as usize)
112 }
113 
114 #[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "solaris")))]
preadv( fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut], offset: u64, ) -> io::Result<usize>115 pub(crate) fn preadv(
116     fd: BorrowedFd<'_>,
117     bufs: &mut [IoSliceMut],
118     offset: u64,
119 ) -> io::Result<usize> {
120     // Silently cast; we'll get `EINVAL` if the value is negative.
121     let offset = offset as i64;
122     let nread = unsafe {
123         ret_ssize_t(libc_preadv(
124             borrowed_fd(fd),
125             bufs.as_ptr().cast::<c::iovec>(),
126             min(bufs.len(), max_iov()) as c::c_int,
127             offset,
128         ))?
129     };
130     Ok(nread as usize)
131 }
132 
133 #[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "solaris")))]
pwritev(fd: BorrowedFd<'_>, bufs: &[IoSlice], offset: u64) -> io::Result<usize>134 pub(crate) fn pwritev(fd: BorrowedFd<'_>, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
135     // Silently cast; we'll get `EINVAL` if the value is negative.
136     let offset = offset as i64;
137     let nwritten = unsafe {
138         ret_ssize_t(libc_pwritev(
139             borrowed_fd(fd),
140             bufs.as_ptr().cast::<c::iovec>(),
141             min(bufs.len(), max_iov()) as c::c_int,
142             offset,
143         ))?
144     };
145     Ok(nwritten as usize)
146 }
147 
148 #[cfg(all(target_os = "linux", target_env = "gnu"))]
preadv2( fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut], offset: u64, flags: ReadWriteFlags, ) -> io::Result<usize>149 pub(crate) fn preadv2(
150     fd: BorrowedFd<'_>,
151     bufs: &mut [IoSliceMut],
152     offset: u64,
153     flags: ReadWriteFlags,
154 ) -> io::Result<usize> {
155     // Silently cast; we'll get `EINVAL` if the value is negative.
156     let offset = offset as i64;
157     let nread = unsafe {
158         ret_ssize_t(libc_preadv2(
159             borrowed_fd(fd),
160             bufs.as_ptr().cast::<c::iovec>(),
161             min(bufs.len(), max_iov()) as c::c_int,
162             offset,
163             flags.bits(),
164         ))?
165     };
166     Ok(nread as usize)
167 }
168 
169 /// At present, `libc` only has `preadv2` defined for glibc. On other
170 /// ABIs, use `libc::syscall`.
171 #[cfg(any(
172     target_os = "android",
173     all(target_os = "linux", not(target_env = "gnu")),
174 ))]
175 #[inline]
preadv2( fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut], offset: u64, flags: ReadWriteFlags, ) -> io::Result<usize>176 pub(crate) fn preadv2(
177     fd: BorrowedFd<'_>,
178     bufs: &mut [IoSliceMut],
179     offset: u64,
180     flags: ReadWriteFlags,
181 ) -> io::Result<usize> {
182     // Silently cast; we'll get `EINVAL` if the value is negative.
183     let offset = offset as i64;
184     let nread = unsafe {
185         ret_ssize_t(libc::syscall(
186             libc::SYS_preadv2,
187             borrowed_fd(fd),
188             bufs.as_ptr().cast::<c::iovec>(),
189             min(bufs.len(), max_iov()) as c::c_int,
190             offset,
191             flags.bits(),
192         ) as c::ssize_t)?
193     };
194     Ok(nread as usize)
195 }
196 
197 #[cfg(all(target_os = "linux", target_env = "gnu"))]
pwritev2( fd: BorrowedFd<'_>, bufs: &[IoSlice], offset: u64, flags: ReadWriteFlags, ) -> io::Result<usize>198 pub(crate) fn pwritev2(
199     fd: BorrowedFd<'_>,
200     bufs: &[IoSlice],
201     offset: u64,
202     flags: ReadWriteFlags,
203 ) -> io::Result<usize> {
204     // Silently cast; we'll get `EINVAL` if the value is negative.
205     let offset = offset as i64;
206     let nwritten = unsafe {
207         ret_ssize_t(libc_pwritev2(
208             borrowed_fd(fd),
209             bufs.as_ptr().cast::<c::iovec>(),
210             min(bufs.len(), max_iov()) as c::c_int,
211             offset,
212             flags.bits(),
213         ))?
214     };
215     Ok(nwritten as usize)
216 }
217 
218 /// At present, `libc` only has `pwritev2` defined for glibc. On other
219 /// ABIs, use `libc::syscall`.
220 #[cfg(any(
221     target_os = "android",
222     all(target_os = "linux", not(target_env = "gnu")),
223 ))]
224 #[inline]
pwritev2( fd: BorrowedFd<'_>, bufs: &[IoSlice], offset: u64, flags: ReadWriteFlags, ) -> io::Result<usize>225 pub(crate) fn pwritev2(
226     fd: BorrowedFd<'_>,
227     bufs: &[IoSlice],
228     offset: u64,
229     flags: ReadWriteFlags,
230 ) -> io::Result<usize> {
231     // Silently cast; we'll get `EINVAL` if the value is negative.
232     let offset = offset as i64;
233     let nwritten = unsafe {
234         ret_ssize_t(libc::syscall(
235             libc::SYS_pwritev2,
236             borrowed_fd(fd),
237             bufs.as_ptr().cast::<c::iovec>(),
238             min(bufs.len(), max_iov()) as c::c_int,
239             offset,
240             flags.bits(),
241         ) as c::ssize_t)?
242     };
243     Ok(nwritten as usize)
244 }
245 
246 // These functions are derived from Rust's library/std/src/sys/unix/fd.rs at
247 // revision a77da2d454e6caa227a85b16410b95f93495e7e0.
248 
249 // The maximum read limit on most POSIX-like systems is `SSIZE_MAX`, with the
250 // man page quoting that if the count of bytes to read is greater than
251 // `SSIZE_MAX` the result is "unspecified".
252 //
253 // On macOS, however, apparently the 64-bit libc is either buggy or
254 // intentionally showing odd behavior by rejecting any read with a size larger
255 // than or equal to `INT_MAX`. To handle both of these the read size is capped
256 // on both platforms.
257 #[cfg(target_os = "macos")]
258 const READ_LIMIT: usize = c::c_int::MAX as usize - 1;
259 #[cfg(not(target_os = "macos"))]
260 const READ_LIMIT: usize = c::ssize_t::MAX as usize;
261 
262 #[cfg(any(
263     target_os = "dragonfly",
264     target_os = "freebsd",
265     target_os = "ios",
266     target_os = "macos",
267     target_os = "netbsd",
268     target_os = "openbsd",
269 ))]
max_iov() -> usize270 const fn max_iov() -> usize {
271     c::IOV_MAX as usize
272 }
273 
274 #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
max_iov() -> usize275 const fn max_iov() -> usize {
276     c::UIO_MAXIOV as usize
277 }
278 
279 #[cfg(not(any(
280     target_os = "android",
281     target_os = "dragonfly",
282     target_os = "emscripten",
283     target_os = "freebsd",
284     target_os = "ios",
285     target_os = "linux",
286     target_os = "macos",
287     target_os = "netbsd",
288     target_os = "openbsd",
289 )))]
max_iov() -> usize290 const fn max_iov() -> usize {
291     16 // The minimum value required by POSIX.
292 }
293 
close(raw_fd: RawFd)294 pub(crate) unsafe fn close(raw_fd: RawFd) {
295     let _ = c::close(raw_fd as c::c_int);
296 }
297 
298 #[cfg(any(target_os = "android", target_os = "linux"))]
eventfd(initval: u32, flags: EventfdFlags) -> io::Result<OwnedFd>299 pub(crate) fn eventfd(initval: u32, flags: EventfdFlags) -> io::Result<OwnedFd> {
300     unsafe { syscall_ret_owned_fd(c::syscall(c::SYS_eventfd2, initval, flags.bits())) }
301 }
302 
303 #[cfg(any(target_os = "android", target_os = "linux"))]
304 #[inline]
ioctl_blksszget(fd: BorrowedFd) -> io::Result<u32>305 pub(crate) fn ioctl_blksszget(fd: BorrowedFd) -> io::Result<u32> {
306     let mut result = MaybeUninit::<c::c_uint>::uninit();
307     unsafe {
308         ret(c::ioctl(borrowed_fd(fd), c::BLKSSZGET, result.as_mut_ptr()))?;
309         Ok(result.assume_init() as u32)
310     }
311 }
312 
313 #[cfg(any(target_os = "android", target_os = "linux"))]
314 #[inline]
ioctl_blkpbszget(fd: BorrowedFd) -> io::Result<u32>315 pub(crate) fn ioctl_blkpbszget(fd: BorrowedFd) -> io::Result<u32> {
316     let mut result = MaybeUninit::<c::c_uint>::uninit();
317     unsafe {
318         ret(c::ioctl(
319             borrowed_fd(fd),
320             c::BLKPBSZGET,
321             result.as_mut_ptr(),
322         ))?;
323         Ok(result.assume_init() as u32)
324     }
325 }
326 
327 #[cfg(not(target_os = "redox"))]
ioctl_fionread(fd: BorrowedFd<'_>) -> io::Result<u64>328 pub(crate) fn ioctl_fionread(fd: BorrowedFd<'_>) -> io::Result<u64> {
329     let mut nread = MaybeUninit::<c::c_int>::uninit();
330     unsafe {
331         ret(c::ioctl(borrowed_fd(fd), c::FIONREAD, nread.as_mut_ptr()))?;
332         // `FIONREAD` returns the number of bytes silently casted to a `c_int`,
333         // even when this is lossy. The best we can do is convert it back to a
334         // `u64` without sign-extending it back first.
335         Ok(u64::from(nread.assume_init() as c::c_uint))
336     }
337 }
338 
ioctl_fionbio(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>339 pub(crate) fn ioctl_fionbio(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
340     unsafe {
341         let data = value as c::c_int;
342         ret(c::ioctl(borrowed_fd(fd), c::FIONBIO, &data))
343     }
344 }
345 
346 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
347 #[cfg(all(feature = "fs", feature = "net"))]
is_read_write(fd: BorrowedFd<'_>) -> io::Result<(bool, bool)>348 pub(crate) fn is_read_write(fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> {
349     let (mut read, mut write) = crate::fs::fd::_is_file_read_write(fd)?;
350     let mut not_socket = false;
351     if read {
352         // Do a `recv` with `PEEK` and `DONTWAIT` for 1 byte. A 0 indicates
353         // the read side is shut down; an `EWOULDBLOCK` indicates the read
354         // side is still open.
355         match unsafe {
356             c::recv(
357                 borrowed_fd(fd),
358                 MaybeUninit::<[u8; 1]>::uninit()
359                     .as_mut_ptr()
360                     .cast::<c::c_void>(),
361                 1,
362                 c::MSG_PEEK | c::MSG_DONTWAIT,
363             )
364         } {
365             0 => read = false,
366             -1 => {
367                 #[allow(unreachable_patterns)] // `EAGAIN` may equal `EWOULDBLOCK`
368                 match errno().0 {
369                     c::EAGAIN | c::EWOULDBLOCK => (),
370                     c::ENOTSOCK => not_socket = true,
371                     err => return Err(io::Errno(err)),
372                 }
373             }
374             _ => (),
375         }
376     }
377     if write && !not_socket {
378         // Do a `send` with `DONTWAIT` for 0 bytes. An `EPIPE` indicates
379         // the write side is shut down.
380         if unsafe { c::send(borrowed_fd(fd), [].as_ptr(), 0, c::MSG_DONTWAIT) } == -1 {
381             #[allow(unreachable_patterns)] // `EAGAIN` may equal `EWOULDBLOCK`
382             match errno().0 {
383                 c::EAGAIN | c::EWOULDBLOCK => (),
384                 c::ENOTSOCK => (),
385                 c::EPIPE => write = false,
386                 err => return Err(io::Errno(err)),
387             }
388         }
389     }
390     Ok((read, write))
391 }
392 
393 #[cfg(target_os = "wasi")]
394 #[cfg(all(feature = "fs", feature = "net"))]
is_read_write(_fd: BorrowedFd<'_>) -> io::Result<(bool, bool)>395 pub(crate) fn is_read_write(_fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> {
396     todo!("Implement is_read_write for WASI in terms of fd_fdstat_get");
397 }
398 
fcntl_getfd(fd: BorrowedFd<'_>) -> io::Result<FdFlags>399 pub(crate) fn fcntl_getfd(fd: BorrowedFd<'_>) -> io::Result<FdFlags> {
400     unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GETFD)).map(FdFlags::from_bits_truncate) }
401 }
402 
fcntl_setfd(fd: BorrowedFd<'_>, flags: FdFlags) -> io::Result<()>403 pub(crate) fn fcntl_setfd(fd: BorrowedFd<'_>, flags: FdFlags) -> io::Result<()> {
404     unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_SETFD, flags.bits())) }
405 }
406 
407 #[cfg(not(target_os = "wasi"))]
fcntl_dupfd_cloexec(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd>408 pub(crate) fn fcntl_dupfd_cloexec(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd> {
409     unsafe { ret_owned_fd(c::fcntl(borrowed_fd(fd), c::F_DUPFD_CLOEXEC, min)) }
410 }
411 
412 #[cfg(not(target_os = "wasi"))]
dup(fd: BorrowedFd<'_>) -> io::Result<OwnedFd>413 pub(crate) fn dup(fd: BorrowedFd<'_>) -> io::Result<OwnedFd> {
414     unsafe { ret_owned_fd(c::dup(borrowed_fd(fd))) }
415 }
416 
417 #[cfg(not(target_os = "wasi"))]
dup2(fd: BorrowedFd<'_>, new: &mut OwnedFd) -> io::Result<()>418 pub(crate) fn dup2(fd: BorrowedFd<'_>, new: &mut OwnedFd) -> io::Result<()> {
419     unsafe { ret_discarded_fd(c::dup2(borrowed_fd(fd), borrowed_fd(new.as_fd()))) }
420 }
421 
422 #[cfg(not(any(
423     target_os = "aix",
424     target_os = "android",
425     target_os = "dragonfly",
426     target_os = "haiku",
427     target_os = "ios",
428     target_os = "macos",
429     target_os = "redox",
430     target_os = "wasi",
431 )))]
dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, flags: DupFlags) -> io::Result<()>432 pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, flags: DupFlags) -> io::Result<()> {
433     unsafe {
434         ret_discarded_fd(c::dup3(
435             borrowed_fd(fd),
436             borrowed_fd(new.as_fd()),
437             flags.bits(),
438         ))
439     }
440 }
441 
442 #[cfg(any(
443     target_os = "android",
444     target_os = "dragonfly",
445     target_os = "haiku",
446     target_os = "ios",
447     target_os = "macos",
448     target_os = "redox",
449 ))]
dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, _flags: DupFlags) -> io::Result<()>450 pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, _flags: DupFlags) -> io::Result<()> {
451     // Android 5.0 has `dup3`, but libc doesn't have bindings. Emulate it
452     // using `dup2`. We don't need to worry about the difference between
453     // `dup2` and `dup3` when the file descriptors are equal because we
454     // have an `&mut OwnedFd` which means `fd` doesn't alias it.
455     dup2(fd, new)
456 }
457 
458 #[cfg(any(target_os = "ios", target_os = "macos"))]
ioctl_fioclex(fd: BorrowedFd<'_>) -> io::Result<()>459 pub(crate) fn ioctl_fioclex(fd: BorrowedFd<'_>) -> io::Result<()> {
460     unsafe { ret(c::ioctl(borrowed_fd(fd), c::FIOCLEX)) }
461 }
462 
463 #[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))]
ioctl_tiocexcl(fd: BorrowedFd) -> io::Result<()>464 pub(crate) fn ioctl_tiocexcl(fd: BorrowedFd) -> io::Result<()> {
465     unsafe { ret(c::ioctl(borrowed_fd(fd), c::TIOCEXCL as _)) }
466 }
467 
468 #[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))]
ioctl_tiocnxcl(fd: BorrowedFd) -> io::Result<()>469 pub(crate) fn ioctl_tiocnxcl(fd: BorrowedFd) -> io::Result<()> {
470     unsafe { ret(c::ioctl(borrowed_fd(fd), c::TIOCNXCL as _)) }
471 }
472 
473 #[cfg(not(target_os = "wasi"))]
pipe() -> io::Result<(OwnedFd, OwnedFd)>474 pub(crate) fn pipe() -> io::Result<(OwnedFd, OwnedFd)> {
475     unsafe {
476         let mut result = MaybeUninit::<[OwnedFd; 2]>::uninit();
477         ret(c::pipe(result.as_mut_ptr().cast::<i32>()))?;
478         let [p0, p1] = result.assume_init();
479         Ok((p0, p1))
480     }
481 }
482 
483 #[cfg(not(any(
484     target_os = "aix",
485     target_os = "haiku",
486     target_os = "ios",
487     target_os = "macos",
488     target_os = "wasi"
489 )))]
pipe_with(flags: PipeFlags) -> io::Result<(OwnedFd, OwnedFd)>490 pub(crate) fn pipe_with(flags: PipeFlags) -> io::Result<(OwnedFd, OwnedFd)> {
491     unsafe {
492         let mut result = MaybeUninit::<[OwnedFd; 2]>::uninit();
493         ret(c::pipe2(result.as_mut_ptr().cast::<i32>(), flags.bits()))?;
494         let [p0, p1] = result.assume_init();
495         Ok((p0, p1))
496     }
497 }
498 
499 #[inline]
poll(fds: &mut [PollFd<'_>], timeout: c::c_int) -> io::Result<usize>500 pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: c::c_int) -> io::Result<usize> {
501     let nfds = fds
502         .len()
503         .try_into()
504         .map_err(|_convert_err| io::Errno::INVAL)?;
505 
506     ret_c_int(unsafe { c::poll(fds.as_mut_ptr().cast(), nfds, timeout) })
507         .map(|nready| nready as usize)
508 }
509 
510 #[cfg(any(target_os = "android", target_os = "linux"))]
511 #[inline]
splice( fd_in: BorrowedFd, off_in: Option<&mut u64>, fd_out: BorrowedFd, off_out: Option<&mut u64>, len: usize, flags: SpliceFlags, ) -> io::Result<usize>512 pub fn splice(
513     fd_in: BorrowedFd,
514     off_in: Option<&mut u64>,
515     fd_out: BorrowedFd,
516     off_out: Option<&mut u64>,
517     len: usize,
518     flags: SpliceFlags,
519 ) -> io::Result<usize> {
520     let off_in = off_in
521         .map(|off| (off as *mut u64).cast())
522         .unwrap_or(ptr::null_mut());
523 
524     let off_out = off_out
525         .map(|off| (off as *mut u64).cast())
526         .unwrap_or(ptr::null_mut());
527 
528     ret_ssize_t(unsafe {
529         c::splice(
530             borrowed_fd(fd_in),
531             off_in,
532             borrowed_fd(fd_out),
533             off_out,
534             len,
535             flags.bits(),
536         )
537     })
538     .map(|spliced| spliced as usize)
539 }
540 
541 #[cfg(any(target_os = "android", target_os = "linux"))]
542 #[inline]
vmsplice( fd: BorrowedFd, bufs: &[IoSliceRaw], flags: SpliceFlags, ) -> io::Result<usize>543 pub unsafe fn vmsplice(
544     fd: BorrowedFd,
545     bufs: &[IoSliceRaw],
546     flags: SpliceFlags,
547 ) -> io::Result<usize> {
548     ret_ssize_t(c::vmsplice(
549         borrowed_fd(fd),
550         bufs.as_ptr().cast::<c::iovec>(),
551         min(bufs.len(), max_iov()),
552         flags.bits(),
553     ))
554     .map(|spliced| spliced as usize)
555 }
556