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