1//! libc syscalls supporting `rustix::io`. 2 3use super::super::c; 4#[cfg(any(target_os = "android", target_os = "linux"))] 5use super::super::conv::syscall_ret_owned_fd; 6use super::super::conv::{ 7 borrowed_fd, ret, ret_c_int, ret_discarded_fd, ret_owned_fd, ret_ssize_t, 8}; 9use super::super::offset::{libc_pread, libc_pwrite}; 10#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "solaris")))] 11use super::super::offset::{libc_preadv, libc_pwritev}; 12#[cfg(all(target_os = "linux", target_env = "gnu"))] 13use super::super::offset::{libc_preadv2, libc_pwritev2}; 14use crate::fd::{AsFd, BorrowedFd, OwnedFd, RawFd}; 15#[cfg(not(any(target_os = "aix", target_os = "wasi")))] 16use 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)))] 24use crate::io::PipeFlags; 25use crate::io::{self, FdFlags, IoSlice, IoSliceMut, PollFd}; 26#[cfg(any(target_os = "android", target_os = "linux"))] 27use crate::io::{EventfdFlags, IoSliceRaw, ReadWriteFlags, SpliceFlags}; 28use core::cmp::min; 29use core::convert::TryInto; 30use core::mem::MaybeUninit; 31#[cfg(any(target_os = "android", target_os = "linux"))] 32use core::ptr; 33#[cfg(all(feature = "fs", feature = "net"))] 34use libc_errno::errno; 35 36pub(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 47pub(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 58pub(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 75pub(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 92pub(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 103pub(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")))] 115pub(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")))] 134pub(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"))] 149pub(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] 176pub(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"))] 198pub(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] 225pub(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")] 258const READ_LIMIT: usize = c::c_int::MAX as usize - 1; 259#[cfg(not(target_os = "macos"))] 260const 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))] 270const fn max_iov() -> usize { 271 c::IOV_MAX as usize 272} 273 274#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] 275const 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)))] 290const fn max_iov() -> usize { 291 16 // The minimum value required by POSIX. 292} 293 294pub(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"))] 299pub(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] 305pub(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] 315pub(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"))] 328pub(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 339pub(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"))] 348pub(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"))] 395pub(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 399pub(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 403pub(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"))] 408pub(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"))] 413pub(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"))] 418pub(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)))] 432pub(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))] 450pub(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"))] 459pub(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")))] 464pub(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")))] 469pub(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"))] 474pub(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)))] 490pub(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] 500pub(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] 512pub 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] 543pub 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