diff --git a/CHANGES.md b/CHANGES.md index 61e937d44..1931ccd09 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -273,5 +273,35 @@ a `[MaybeUninit]` instead of a `[u8]`. [`SendAncillaryBuffer`]: https://docs.rs/rustix/1.0.0/rustix/net/struct.SendAncillaryBuffer.html [`RecvAncillaryBuffer`]: https://docs.rs/rustix/1.0.0/rustix/net/struct.RecvAncillaryBuffer.html +[`read`], [`pread`], [`recv`], [`recvfrom`], [`getrandom`], [`epoll::wait`], +[`kqueue`], [`getxattr`], [`lgetxattr`], [`fgetxattr`], [`listxattr`], +[`llistxattr`], and [`flistxattr`] now use the new [`Buffer` trait]. + +This replaces `read_uninit`, `pread_uninit`, `recv_uninit`, `recvfrom_uninit`, +and `getrandom_uninit`, as the `Buffer` trait supports reading into +uninitialized slices. + +`epoll::wait` and `kqueue` previously took a `Vec` which it implicitly cleared +before results were appended. When passing a `Vec` to `epoll::wait` or `kqueue` +using [`spare_capacity`], the `Vec` is not cleared first. Consider clearing the +vector before calling `epoll::wait` or `kqueue`, or consuming it using +`.drain(..)` before reusing it. + +[`read`]: https://docs.rs/rustix/1.0.0/rustix/io/fn.read.html +[`pread`]: https://docs.rs/rustix/1.0.0/rustix/io/fn.pread.html +[`recv`]: https://docs.rs/rustix/1.0.0/rustix/net/fn.recv.html +[`recvfrom`]: https://docs.rs/rustix/1.0.0/rustix/net/fn.recvfrom.html +[`getrandom`]: https://docs.rs/rustix/1.0.0/rustix/rand/fn.getrandom.html +[`epoll::wait`]: https://docs.rs/rustix/1.0.0/rustix/event/epoll/fn.wait.html +[`getxattr`]: https://docs.rs/rustix/1.0.0/rustix/fs/fn.getxattr.html +[`lgetxattr`]: https://docs.rs/rustix/1.0.0/rustix/fs/fn.lgetxattr.html +[`fgetxattr`]: https://docs.rs/rustix/1.0.0/rustix/fs/fn.fgetxattr.html +[`listxattr`]: https://docs.rs/rustix/1.0.0/rustix/fs/fn.listxattr.html +[`llistxattr`]: https://docs.rs/rustix/1.0.0/rustix/fs/fn.llistxattr.html +[`flistxattr`]: https://docs.rs/rustix/1.0.0/rustix/fs/fn.flistxattr.html +[`kqueue`]: https://docs.rs/rustix/1.0.0/x86_64-unknown-freebsd/rustix/event/kqueue/fn.kqueue.html +[`Buffer` trait]: https://docs.rs/rustix/1.0.0/rustix/buffer/trait.Buffer.html +[`spare_capacity`]: https://docs.rs/rustix/1.0.0/rustix/buffer/fn.spare_capacity.html + All explicitly deprecated functions and types have been removed. Their deprecation messages will have identified alternatives. diff --git a/examples/kq.rs b/examples/kq.rs index a2209ce9b..e9d061c34 100644 --- a/examples/kq.rs +++ b/examples/kq.rs @@ -2,6 +2,7 @@ #[cfg(all(bsd, feature = "event"))] fn main() -> std::io::Result<()> { + use rustix::buffer::spare_capacity; use rustix::event::kqueue::*; #[cfg(feature = "fs")] use rustix::{fd::AsRawFd, fs}; @@ -60,7 +61,7 @@ fn main() -> std::io::Result<()> { eprintln!("Run with --features process to enable more!"); #[cfg(not(feature = "fs"))] eprintln!("Run with --features fs to enable more!"); - unsafe { kevent(&kq, &subs, &mut out, None) }?; + unsafe { kevent(&kq, &subs, spare_capacity(&mut out), None) }?; loop { while let Some(e) = out.pop() { @@ -80,7 +81,7 @@ fn main() -> std::io::Result<()> { _ => eprintln!("Unknown event"), } } - unsafe { kevent(&kq, &[], &mut out, None) }?; + unsafe { kevent(&kq, &[], spare_capacity(&mut out), None) }?; } } diff --git a/src/backend/libc/event/syscalls.rs b/src/backend/libc/event/syscalls.rs index 92ec2915f..8c126bf9f 100644 --- a/src/backend/libc/event/syscalls.rs +++ b/src/backend/libc/event/syscalls.rs @@ -4,7 +4,6 @@ use crate::backend::c; #[cfg(any(linux_kernel, solarish, target_os = "redox"))] use crate::backend::conv::ret; use crate::backend::conv::ret_c_int; -#[cfg(feature = "alloc")] #[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))] use crate::backend::conv::ret_u32; #[cfg(solarish)] @@ -24,11 +23,7 @@ use crate::io; use crate::utils::as_mut_ptr; #[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))] use crate::utils::as_ptr; -#[cfg(any( - all(feature = "alloc", bsd), - solarish, - all(feature = "alloc", any(linux_kernel, target_os = "redox")), -))] +#[cfg(solarish)] use core::mem::MaybeUninit; #[cfg(any( bsd, @@ -107,7 +102,7 @@ pub(crate) fn kqueue() -> io::Result { pub(crate) unsafe fn kevent( kq: BorrowedFd<'_>, changelist: &[Event], - eventlist: &mut [MaybeUninit], + eventlist: (*mut Event, usize), timeout: Option<&c::timespec>, ) -> io::Result { ret_c_int(c::kevent( @@ -117,11 +112,8 @@ pub(crate) unsafe fn kevent( .len() .try_into() .map_err(|_| io::Errno::OVERFLOW)?, - eventlist.as_mut_ptr().cast(), - eventlist - .len() - .try_into() - .map_err(|_| io::Errno::OVERFLOW)?, + eventlist.0.cast(), + eventlist.1.try_into().map_err(|_| io::Errno::OVERFLOW)?, timeout.map_or(null(), as_ptr), )) } @@ -512,11 +504,10 @@ pub(crate) fn epoll_del(epoll: BorrowedFd<'_>, source: BorrowedFd<'_>) -> io::Re } #[inline] -#[cfg(feature = "alloc")] #[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))] -pub(crate) fn epoll_wait( +pub(crate) unsafe fn epoll_wait( epoll: BorrowedFd<'_>, - events: &mut [MaybeUninit], + events: (*mut crate::event::epoll::Event, usize), timeout: Option<&Timespec>, ) -> io::Result { // If we're on Linux >= 5.11 and a libc that has an `epoll_pwait2` @@ -527,7 +518,7 @@ pub(crate) fn epoll_wait( target_env = "gnu", not(fix_y2038) ))] - unsafe { + { weak! { fn epoll_pwait2( c::c_int, @@ -541,8 +532,8 @@ pub(crate) fn epoll_wait( if let Some(epoll_pwait2_func) = epoll_pwait2.get() { return ret_u32(epoll_pwait2_func( borrowed_fd(epoll), - events.as_mut_ptr().cast::(), - events.len().try_into().unwrap_or(i32::MAX), + events.0.cast::(), + events.1.try_into().unwrap_or(i32::MAX), crate::utils::option_as_ptr(timeout).cast(), null(), )) @@ -552,7 +543,7 @@ pub(crate) fn epoll_wait( // If we're on Linux >= 5.11, use `epoll_pwait2` via `libc::syscall`. #[cfg(all(linux_kernel, feature = "linux_5_11"))] - unsafe { + { use linux_raw_sys::general::__kernel_timespec as timespec; syscall! { @@ -567,8 +558,8 @@ pub(crate) fn epoll_wait( ret_u32(epoll_pwait2( borrowed_fd(epoll), - events.as_mut_ptr().cast::(), - events.len().try_into().unwrap_or(i32::MAX), + events.0.cast::(), + events.1.try_into().unwrap_or(i32::MAX), crate::utils::option_as_ptr(timeout).cast(), null(), )) @@ -577,7 +568,7 @@ pub(crate) fn epoll_wait( // Otherwise just use `epoll_wait`. #[cfg(not(all(linux_kernel, feature = "linux_5_11")))] - unsafe { + { let timeout = match timeout { None => -1, Some(timeout) => timeout.as_c_int_millis().ok_or(io::Errno::INVAL)?, @@ -585,8 +576,8 @@ pub(crate) fn epoll_wait( ret_u32(c::epoll_wait( borrowed_fd(epoll), - events.as_mut_ptr().cast::(), - events.len().try_into().unwrap_or(i32::MAX), + events.0.cast::(), + events.1.try_into().unwrap_or(i32::MAX), timeout, )) .map(|i| i as usize) diff --git a/src/backend/libc/fs/syscalls.rs b/src/backend/libc/fs/syscalls.rs index 6c4bf52ba..b916c7b6f 100644 --- a/src/backend/libc/fs/syscalls.rs +++ b/src/backend/libc/fs/syscalls.rs @@ -2329,16 +2329,18 @@ struct Attrlist { } #[cfg(any(apple, linux_kernel, target_os = "hurd"))] -pub(crate) fn getxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result { - let value_ptr = value.as_mut_ptr(); - +pub(crate) unsafe fn getxattr( + path: &CStr, + name: &CStr, + value: (*mut u8, usize), +) -> io::Result { #[cfg(not(apple))] - unsafe { + { ret_usize(c::getxattr( path.as_ptr(), name.as_ptr(), - value_ptr.cast::(), - value.len(), + value.0.cast::(), + value.1, )) } @@ -2346,35 +2348,35 @@ pub(crate) fn getxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result { // Passing an empty to slice to `getxattr` leads to `ERANGE` on macOS. // Pass null instead. - let ptr = if value.is_empty() { + let ptr = if value.1 == 0 { core::ptr::null_mut() } else { - value_ptr.cast::() + value.0.cast::() }; - unsafe { - ret_usize(c::getxattr( - path.as_ptr(), - name.as_ptr(), - ptr, - value.len(), - 0, - 0, - )) - } + ret_usize(c::getxattr( + path.as_ptr(), + name.as_ptr(), + ptr, + value.1, + 0, + 0, + )) } } #[cfg(any(apple, linux_kernel, target_os = "hurd"))] -pub(crate) fn lgetxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result { - let value_ptr = value.as_mut_ptr(); - +pub(crate) unsafe fn lgetxattr( + path: &CStr, + name: &CStr, + value: (*mut u8, usize), +) -> io::Result { #[cfg(not(apple))] - unsafe { + { ret_usize(c::lgetxattr( path.as_ptr(), name.as_ptr(), - value_ptr.cast::(), - value.len(), + value.0.cast::(), + value.1, )) } @@ -2382,36 +2384,36 @@ pub(crate) fn lgetxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Resul { // Passing an empty to slice to `getxattr` leads to `ERANGE` on macOS. // Pass null instead. - let ptr = if value.is_empty() { + let ptr = if value.1 == 0 { core::ptr::null_mut() } else { - value_ptr.cast::() + value.0.cast::() }; - unsafe { - ret_usize(c::getxattr( - path.as_ptr(), - name.as_ptr(), - ptr, - value.len(), - 0, - c::XATTR_NOFOLLOW, - )) - } + ret_usize(c::getxattr( + path.as_ptr(), + name.as_ptr(), + ptr, + value.1, + 0, + c::XATTR_NOFOLLOW, + )) } } #[cfg(any(apple, linux_kernel, target_os = "hurd"))] -pub(crate) fn fgetxattr(fd: BorrowedFd<'_>, name: &CStr, value: &mut [u8]) -> io::Result { - let value_ptr = value.as_mut_ptr(); - +pub(crate) unsafe fn fgetxattr( + fd: BorrowedFd<'_>, + name: &CStr, + value: (*mut u8, usize), +) -> io::Result { #[cfg(not(apple))] - unsafe { + { ret_usize(c::fgetxattr( borrowed_fd(fd), name.as_ptr(), - value_ptr.cast::(), - value.len(), + value.0.cast::(), + value.1, )) } @@ -2419,21 +2421,19 @@ pub(crate) fn fgetxattr(fd: BorrowedFd<'_>, name: &CStr, value: &mut [u8]) -> io { // Passing an empty to slice to `getxattr` leads to `ERANGE` on macOS. // Pass null instead. - let ptr = if value.is_empty() { + let ptr = if value.1 == 0 { core::ptr::null_mut() } else { - value_ptr.cast::() + value.0.cast::() }; - unsafe { - ret_usize(c::fgetxattr( - borrowed_fd(fd), - name.as_ptr(), - ptr, - value.len(), - 0, - 0, - )) - } + ret_usize(c::fgetxattr( + borrowed_fd(fd), + name.as_ptr(), + ptr, + value.1, + 0, + 0, + )) } } @@ -2531,70 +2531,61 @@ pub(crate) fn fsetxattr( } #[cfg(any(apple, linux_kernel, target_os = "hurd"))] -pub(crate) fn listxattr(path: &CStr, list: &mut [u8]) -> io::Result { +pub(crate) unsafe fn listxattr(path: &CStr, list: (*mut u8, usize)) -> io::Result { #[cfg(not(apple))] - unsafe { + { ret_usize(c::listxattr( path.as_ptr(), - list.as_mut_ptr().cast::(), - list.len(), + list.0.cast::(), + list.1, )) } #[cfg(apple)] - unsafe { + { ret_usize(c::listxattr( path.as_ptr(), - list.as_mut_ptr().cast::(), - list.len(), + list.0.cast::(), + list.1, 0, )) } } #[cfg(any(apple, linux_kernel, target_os = "hurd"))] -pub(crate) fn llistxattr(path: &CStr, list: &mut [u8]) -> io::Result { +pub(crate) unsafe fn llistxattr(path: &CStr, list: (*mut u8, usize)) -> io::Result { #[cfg(not(apple))] - unsafe { + { ret_usize(c::llistxattr( path.as_ptr(), - list.as_mut_ptr().cast::(), - list.len(), + list.0.cast::(), + list.1, )) } #[cfg(apple)] - unsafe { + { ret_usize(c::listxattr( path.as_ptr(), - list.as_mut_ptr().cast::(), - list.len(), + list.0.cast::(), + list.1, c::XATTR_NOFOLLOW, )) } } #[cfg(any(apple, linux_kernel, target_os = "hurd"))] -pub(crate) fn flistxattr(fd: BorrowedFd<'_>, list: &mut [u8]) -> io::Result { +pub(crate) unsafe fn flistxattr(fd: BorrowedFd<'_>, list: (*mut u8, usize)) -> io::Result { let fd = borrowed_fd(fd); #[cfg(not(apple))] - unsafe { - ret_usize(c::flistxattr( - fd, - list.as_mut_ptr().cast::(), - list.len(), - )) + { + ret_usize(c::flistxattr(fd, list.0.cast::(), list.1)) } #[cfg(apple)] - unsafe { - ret_usize(c::flistxattr( - fd, - list.as_mut_ptr().cast::(), - list.len(), - 0, - )) + { + ret_usize(c::flistxattr(fd, list.0.cast::(), list.1, 0)) } } diff --git a/src/backend/libc/io/syscalls.rs b/src/backend/libc/io/syscalls.rs index e32a432ba..9044ddbfa 100644 --- a/src/backend/libc/io/syscalls.rs +++ b/src/backend/libc/io/syscalls.rs @@ -24,8 +24,12 @@ use { crate::io::{IoSlice, IoSliceMut}, }; -pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: *mut u8, len: usize) -> io::Result { - ret_usize(c::read(borrowed_fd(fd), buf.cast(), min(len, READ_LIMIT))) +pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: (*mut u8, usize)) -> io::Result { + ret_usize(c::read( + borrowed_fd(fd), + buf.0.cast(), + min(buf.1, READ_LIMIT), + )) } pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result { @@ -40,11 +44,10 @@ pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result { pub(crate) unsafe fn pread( fd: BorrowedFd<'_>, - buf: *mut u8, - len: usize, + buf: (*mut u8, usize), offset: u64, ) -> io::Result { - let len = min(len, READ_LIMIT); + let len = min(buf.1, READ_LIMIT); // Silently cast; we'll get `EINVAL` if the value is negative. let offset = offset as i64; @@ -53,7 +56,7 @@ pub(crate) unsafe fn pread( #[cfg(any(target_os = "espidf", target_os = "vita"))] let offset: i32 = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?; - ret_usize(c::pread(borrowed_fd(fd), buf.cast(), len, offset)) + ret_usize(c::pread(borrowed_fd(fd), buf.0.cast(), len, offset)) } pub(crate) fn pwrite(fd: BorrowedFd<'_>, buf: &[u8], offset: u64) -> io::Result { diff --git a/src/backend/libc/io/windows_syscalls.rs b/src/backend/libc/io/windows_syscalls.rs index 6e451a9f1..b26e726cf 100644 --- a/src/backend/libc/io/windows_syscalls.rs +++ b/src/backend/libc/io/windows_syscalls.rs @@ -3,12 +3,34 @@ use crate::backend::c; #[cfg(feature = "try_close")] use crate::backend::conv::ret; -use crate::backend::conv::{borrowed_fd, ret_c_int}; +use crate::backend::conv::{borrowed_fd, ret_c_int, ret_send_recv, send_recv_len}; use crate::backend::fd::LibcFd; use crate::fd::{BorrowedFd, RawFd}; use crate::io; use crate::ioctl::{IoctlOutput, RawOpcode}; +pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: (*mut u8, usize)) -> io::Result { + // `read` on a socket is equivalent to `recv` with no flags. + ret_send_recv(c::recv( + borrowed_fd(fd), + buf.0.cast(), + send_recv_len(buf.1), + 0, + )) +} + +pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result { + // `write` on a socket is equivalent to `send` with no flags. + unsafe { + ret_send_recv(c::send( + borrowed_fd(fd), + buf.as_ptr().cast(), + send_recv_len(buf.len()), + 0, + )) + } +} + pub(crate) unsafe fn close(raw_fd: RawFd) { let _ = c::close(raw_fd as LibcFd); } diff --git a/src/backend/libc/net/syscalls.rs b/src/backend/libc/net/syscalls.rs index 15f25fc5f..08e96560a 100644 --- a/src/backend/libc/net/syscalls.rs +++ b/src/backend/libc/net/syscalls.rs @@ -27,14 +27,13 @@ use { pub(crate) unsafe fn recv( fd: BorrowedFd<'_>, - buf: *mut u8, - len: usize, + buf: (*mut u8, usize), flags: RecvFlags, ) -> io::Result { ret_send_recv(c::recv( borrowed_fd(fd), - buf.cast(), - send_recv_len(len), + buf.0.cast(), + send_recv_len(buf.1), bitflags_bits!(flags), )) } @@ -52,8 +51,7 @@ pub(crate) fn send(fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags) -> io::Resu pub(crate) unsafe fn recvfrom( fd: BorrowedFd<'_>, - buf: *mut u8, - buf_len: usize, + buf: (*mut u8, usize), flags: RecvFlags, ) -> io::Result<(usize, Option)> { let mut addr = SocketAddrBuf::new(); @@ -65,8 +63,8 @@ pub(crate) unsafe fn recvfrom( let nread = ret_send_recv(c::recvfrom( borrowed_fd(fd), - buf.cast(), - send_recv_len(buf_len), + buf.0.cast(), + send_recv_len(buf.1), bitflags_bits!(flags), addr.storage.as_mut_ptr().cast::(), &mut addr.len, diff --git a/src/backend/libc/rand/syscalls.rs b/src/backend/libc/rand/syscalls.rs index 3a3929e7d..ce17c6aa5 100644 --- a/src/backend/libc/rand/syscalls.rs +++ b/src/backend/libc/rand/syscalls.rs @@ -4,15 +4,11 @@ use {crate::backend::c, crate::backend::conv::ret_usize, crate::io, crate::rand::GetRandomFlags}; #[cfg(linux_kernel)] -pub(crate) unsafe fn getrandom( - buf: *mut u8, - cap: usize, - flags: GetRandomFlags, -) -> io::Result { +pub(crate) unsafe fn getrandom(buf: (*mut u8, usize), flags: GetRandomFlags) -> io::Result { // `getrandom` wasn't supported in glibc until 2.25. weak_or_syscall! { fn getrandom(buf: *mut c::c_void, buflen: c::size_t, flags: c::c_uint) via SYS_getrandom -> c::ssize_t } - ret_usize(getrandom(buf.cast(), cap, flags.bits())) + ret_usize(getrandom(buf.0.cast(), buf.1, flags.bits())) } diff --git a/src/backend/linux_raw/event/syscalls.rs b/src/backend/linux_raw/event/syscalls.rs index f0e5004e7..3daf84076 100644 --- a/src/backend/linux_raw/event/syscalls.rs +++ b/src/backend/linux_raw/event/syscalls.rs @@ -6,8 +6,8 @@ #![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] use crate::backend::conv::{ - by_ref, c_int, c_uint, opt_ref, ret, ret_c_int, ret_error, ret_owned_fd, ret_usize, size_of, - slice_mut, zero, + by_ref, c_int, c_uint, opt_ref, pass_usize, ret, ret_c_int, ret_error, ret_owned_fd, ret_usize, + size_of, slice_mut, zero, }; use crate::event::{epoll, EventfdFlags, FdSetElement, PollFd, Timespec}; use crate::fd::{BorrowedFd, OwnedFd}; @@ -15,8 +15,6 @@ use crate::io; use crate::utils::as_mut_ptr; #[cfg(feature = "linux_5_11")] use crate::utils::option_as_ptr; -#[cfg(feature = "alloc")] -use core::mem::MaybeUninit; use core::ptr::null_mut; use linux_raw_sys::general::{kernel_sigset_t, EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD}; @@ -174,32 +172,25 @@ pub(crate) fn epoll_del(epfd: BorrowedFd<'_>, fd: BorrowedFd<'_>) -> io::Result< } } -#[cfg(feature = "alloc")] #[inline] -pub(crate) fn epoll_wait( +pub(crate) unsafe fn epoll_wait( epfd: BorrowedFd<'_>, - events: &mut [MaybeUninit], + events: (*mut crate::event::epoll::Event, usize), timeout: Option<&Timespec>, ) -> io::Result { - let (buf_addr_mut, buf_len) = slice_mut(events); - // If we have Linux 5.11, use `epoll_pwait2`, which takes a `timespec`. #[cfg(feature = "linux_5_11")] { let timeout = option_as_ptr(timeout); - // SAFETY: `__NR_epoll_pwait2` doesn't access any user memory outside of - // the `events` array, as we don't pass it a `sigmask`. - unsafe { - ret_usize(syscall!( - __NR_epoll_pwait2, - epfd, - buf_addr_mut, - buf_len, - timeout, - zero() - )) - } + ret_usize(syscall!( + __NR_epoll_pwait2, + epfd, + events.0, + pass_usize(events.1), + timeout, + zero() + )) } // If we don't have Linux 5.11, use `epoll_pwait`, which takes a `c_int`. @@ -214,18 +205,14 @@ pub(crate) fn epoll_wait( Some(timeout) => timeout.as_c_int_millis().ok_or(io::Errno::INVAL)?, }; - // SAFETY: `__NR_epoll_pwait` doesn't access any user memory outside of - // the `events` array, as we don't pass it a `sigmask`. - unsafe { - ret_usize(syscall!( - __NR_epoll_pwait, - epfd, - buf_addr_mut, - buf_len, - c_int(timeout), - zero() - )) - } + ret_usize(syscall!( + __NR_epoll_pwait, + epfd, + events.0, + pass_usize(events.1), + c_int(timeout), + zero() + )) } } diff --git a/src/backend/linux_raw/fs/syscalls.rs b/src/backend/linux_raw/fs/syscalls.rs index 3851f9c86..2d4dde054 100644 --- a/src/backend/linux_raw/fs/syscalls.rs +++ b/src/backend/linux_raw/fs/syscalls.rs @@ -1534,45 +1534,48 @@ pub(crate) fn inotify_rm_watch(infd: BorrowedFd<'_>, wfd: i32) -> io::Result<()> } #[inline] -pub(crate) fn getxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result { - let (value_addr_mut, value_len) = slice_mut(value); - unsafe { - ret_usize(syscall!( - __NR_getxattr, - path, - name, - value_addr_mut, - value_len - )) - } +pub(crate) unsafe fn getxattr( + path: &CStr, + name: &CStr, + value: (*mut u8, usize), +) -> io::Result { + ret_usize(syscall!( + __NR_getxattr, + path, + name, + value.0, + pass_usize(value.1) + )) } #[inline] -pub(crate) fn lgetxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result { - let (value_addr_mut, value_len) = slice_mut(value); - unsafe { - ret_usize(syscall!( - __NR_lgetxattr, - path, - name, - value_addr_mut, - value_len - )) - } +pub(crate) unsafe fn lgetxattr( + path: &CStr, + name: &CStr, + value: (*mut u8, usize), +) -> io::Result { + ret_usize(syscall!( + __NR_lgetxattr, + path, + name, + value.0, + pass_usize(value.1) + )) } #[inline] -pub(crate) fn fgetxattr(fd: BorrowedFd<'_>, name: &CStr, value: &mut [u8]) -> io::Result { - let (value_addr_mut, value_len) = slice_mut(value); - unsafe { - ret_usize(syscall!( - __NR_fgetxattr, - fd, - name, - value_addr_mut, - value_len - )) - } +pub(crate) unsafe fn fgetxattr( + fd: BorrowedFd<'_>, + name: &CStr, + value: (*mut u8, usize), +) -> io::Result { + ret_usize(syscall!( + __NR_fgetxattr, + fd, + name, + value.0, + pass_usize(value.1) + )) } #[inline] @@ -1636,21 +1639,18 @@ pub(crate) fn fsetxattr( } #[inline] -pub(crate) fn listxattr(path: &CStr, list: &mut [u8]) -> io::Result { - let (list_addr_mut, list_len) = slice_mut(list); - unsafe { ret_usize(syscall!(__NR_listxattr, path, list_addr_mut, list_len)) } +pub(crate) unsafe fn listxattr(path: &CStr, list: (*mut u8, usize)) -> io::Result { + ret_usize(syscall!(__NR_listxattr, path, list.0, pass_usize(list.1))) } #[inline] -pub(crate) fn llistxattr(path: &CStr, list: &mut [u8]) -> io::Result { - let (list_addr_mut, list_len) = slice_mut(list); - unsafe { ret_usize(syscall!(__NR_llistxattr, path, list_addr_mut, list_len)) } +pub(crate) unsafe fn llistxattr(path: &CStr, list: (*mut u8, usize)) -> io::Result { + ret_usize(syscall!(__NR_llistxattr, path, list.0, pass_usize(list.1))) } #[inline] -pub(crate) fn flistxattr(fd: BorrowedFd<'_>, list: &mut [u8]) -> io::Result { - let (list_addr_mut, list_len) = slice_mut(list); - unsafe { ret_usize(syscall!(__NR_flistxattr, fd, list_addr_mut, list_len)) } +pub(crate) unsafe fn flistxattr(fd: BorrowedFd<'_>, list: (*mut u8, usize)) -> io::Result { + ret_usize(syscall!(__NR_flistxattr, fd, list.0, pass_usize(list.1))) } #[inline] diff --git a/src/backend/linux_raw/io/syscalls.rs b/src/backend/linux_raw/io/syscalls.rs index 95799c8a3..f9319ff7b 100644 --- a/src/backend/linux_raw/io/syscalls.rs +++ b/src/backend/linux_raw/io/syscalls.rs @@ -27,15 +27,14 @@ use core::cmp; use linux_raw_sys::general::{F_DUPFD_CLOEXEC, F_GETFD, F_SETFD}; #[inline] -pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: *mut u8, len: usize) -> io::Result { - ret_usize(syscall!(__NR_read, fd, buf, pass_usize(len))) +pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: (*mut u8, usize)) -> io::Result { + ret_usize(syscall!(__NR_read, fd, buf.0, pass_usize(buf.1))) } #[inline] pub(crate) unsafe fn pread( fd: BorrowedFd<'_>, - buf: *mut u8, - len: usize, + buf: (*mut u8, usize), pos: u64, ) -> io::Result { // @@ -47,8 +46,8 @@ pub(crate) unsafe fn pread( ret_usize(syscall!( __NR_pread64, fd, - buf, - pass_usize(len), + buf.0, + pass_usize(buf.1), zero(), hi(pos), lo(pos) @@ -62,8 +61,8 @@ pub(crate) unsafe fn pread( ret_usize(syscall!( __NR_pread64, fd, - buf, - pass_usize(len), + buf.0, + pass_usize(buf.1), hi(pos), lo(pos) )) @@ -72,8 +71,8 @@ pub(crate) unsafe fn pread( ret_usize(syscall!( __NR_pread64, fd, - buf, - pass_usize(len), + buf.0, + pass_usize(buf.1), loff_t_from_u64(pos) )) } diff --git a/src/backend/linux_raw/net/syscalls.rs b/src/backend/linux_raw/net/syscalls.rs index 8efce479c..488e08f02 100644 --- a/src/backend/linux_raw/net/syscalls.rs +++ b/src/backend/linux_raw/net/syscalls.rs @@ -477,8 +477,7 @@ pub(crate) fn sendto( #[inline] pub(crate) unsafe fn recv( fd: BorrowedFd<'_>, - buf: *mut u8, - len: usize, + buf: (*mut u8, usize), flags: RecvFlags, ) -> io::Result { #[cfg(not(any( @@ -491,7 +490,7 @@ pub(crate) unsafe fn recv( target_arch = "x86_64", )))] { - ret_usize(syscall!(__NR_recv, fd, buf, pass_usize(len), flags)) + ret_usize(syscall!(__NR_recv, fd, buf.0, pass_usize(buf.1), flags)) } #[cfg(any( target_arch = "aarch64", @@ -505,8 +504,8 @@ pub(crate) unsafe fn recv( ret_usize(syscall!( __NR_recvfrom, fd, - buf, - pass_usize(len), + buf.0, + pass_usize(buf.1), flags, zero(), zero() @@ -519,8 +518,8 @@ pub(crate) unsafe fn recv( x86_sys(SYS_RECV), slice_just_addr::, _>(&[ fd.into(), - buf.into(), - pass_usize(len), + buf.0.into(), + pass_usize(buf.1), flags.into(), ]) )) @@ -530,8 +529,7 @@ pub(crate) unsafe fn recv( #[inline] pub(crate) unsafe fn recvfrom( fd: BorrowedFd<'_>, - buf: *mut u8, - len: usize, + buf: (*mut u8, usize), flags: RecvFlags, ) -> io::Result<(usize, Option)> { let mut addr = SocketAddrBuf::new(); @@ -545,8 +543,8 @@ pub(crate) unsafe fn recvfrom( let nread = ret_usize(syscall!( __NR_recvfrom, fd, - buf, - pass_usize(len), + buf.0, + pass_usize(buf.1), flags, &mut addr.storage, by_mut(&mut addr.len) @@ -557,8 +555,8 @@ pub(crate) unsafe fn recvfrom( x86_sys(SYS_RECVFROM), slice_just_addr::, _>(&[ fd.into(), - buf.into(), - pass_usize(len), + buf.0.into(), + pass_usize(buf.1), flags.into(), (&mut addr.storage).into(), by_mut(&mut addr.len), diff --git a/src/backend/linux_raw/param/auxv.rs b/src/backend/linux_raw/param/auxv.rs index 2e7ffbb3e..2f0181e30 100644 --- a/src/backend/linux_raw/param/auxv.rs +++ b/src/backend/linux_raw/param/auxv.rs @@ -569,7 +569,7 @@ impl Iterator for AuxFile { let mut buf = [0_u8; size_of::()]; let mut slice = &mut buf[..]; while !slice.is_empty() { - match crate::io::read(&self.0, slice) { + match crate::io::read(&self.0, &mut *slice) { Ok(0) => panic!("unexpected end of auxv file"), Ok(n) => slice = &mut slice[n..], Err(crate::io::Errno::INTR) => continue, diff --git a/src/backend/linux_raw/rand/syscalls.rs b/src/backend/linux_raw/rand/syscalls.rs index c0f497b98..acea3968c 100644 --- a/src/backend/linux_raw/rand/syscalls.rs +++ b/src/backend/linux_raw/rand/syscalls.rs @@ -10,10 +10,6 @@ use crate::io; use crate::rand::GetRandomFlags; #[inline] -pub(crate) unsafe fn getrandom( - buf: *mut u8, - cap: usize, - flags: GetRandomFlags, -) -> io::Result { - ret_usize(syscall!(__NR_getrandom, buf, pass_usize(cap), flags)) +pub(crate) unsafe fn getrandom(buf: (*mut u8, usize), flags: GetRandomFlags) -> io::Result { + ret_usize(syscall!(__NR_getrandom, buf.0, pass_usize(buf.1), flags)) } diff --git a/src/buffer.rs b/src/buffer.rs index bb31ac056..5ed32a61d 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -2,73 +2,407 @@ #![allow(unsafe_code)] +#[cfg(feature = "alloc")] +use alloc::vec::Vec; use core::mem::MaybeUninit; use core::slice; -/// Split an uninitialized byte slice into initialized and uninitialized parts. -/// -/// # Safety -/// -/// `init_len` must not be greater than `buf.len()`, and at least `init_len` -/// bytes must be initialized. -#[inline] -pub(super) unsafe fn split_init( - buf: &mut [MaybeUninit], - init_len: usize, -) -> (&mut [u8], &mut [MaybeUninit]) { - debug_assert!(init_len <= buf.len()); - let buf_ptr = buf.as_mut_ptr(); - let uninit_len = buf.len() - init_len; - let init = slice::from_raw_parts_mut(buf_ptr.cast::(), init_len); - let uninit = slice::from_raw_parts_mut(buf_ptr.add(init_len), uninit_len); - (init, uninit) +/// A memory buffer that may be uninitialized. +/// +/// There are three types that implement the `Buffer` trait, and the type you +/// use determines the return type of the functions that use it: +/// +/// | If you pass a... | You get back a... | +/// | ------------------------ | ----------------- | +/// | `&mut [u8]` | `usize`, indicating the number of elements initialized. | +/// | `&mut [MaybeUninit]` | `(&mut [u8], &[mut MaybeUninit])`, holding the initialized and uninitialized subslices. | +/// | [`SpareCapacity`] | `usize`, indicating the number of elements initialized. And the `Vec` is extended. | +/// +/// # Examples +/// +/// Passing a `&mut [u8]`: +/// +/// ```rust +/// # use rustix::io::read; +/// # fn example(fd: rustix::fd::BorrowedFd) -> rustix::io::Result<()> { +/// let mut buf = [0_u8; 64]; +/// let nread = read(fd, &mut buf)?; +/// # Ok(()) +/// # } +/// ``` +/// +/// Passing a `&mut [MaybeUninit]`: +/// +/// ```rust +/// # use rustix::io::read; +/// # use core::mem::MaybeUninit; +/// # fn example(fd: rustix::fd::BorrowedFd) -> rustix::io::Result<()> { +/// let mut buf = [MaybeUninit::::uninit(); 64]; +/// let (init, uninit) = read(fd, &mut buf)?; +/// // `init` is a `&mut [u8]` with the initialized bytes. +/// // `uninit` is a `&mut [MaybeUninit]` with the remaining bytes. +/// # Ok(()) +/// # } +/// ``` +/// +/// Passing a [`SpareCapacity`], via the [`spare_capacity`] helper function: +/// +/// ```rust +/// # use rustix::io::read; +/// # use rustix::buffer::spare_capacity; +/// # fn example(fd: rustix::fd::BorrowedFd) -> rustix::io::Result<()> { +/// let mut buf = Vec::with_capacity(64); +/// let nread = read(fd, spare_capacity(&mut buf))?; +/// // Also, `buf.len()` is now `nread` elements greater. +/// # Ok(()) +/// # } +/// ``` +/// +/// If you see errors like "move occurs because `x` has type `&mut [u8]`, +/// which does not implement the `Copy` trait", replace `x` with `&mut *x`. +pub trait Buffer: private::Sealed {} + +// Implement `Buffer` for all the types that implement `Sealed`. +impl Buffer for &mut [T] {} +impl Buffer for &mut [T; N] {} +#[cfg(feature = "alloc")] +impl Buffer for &mut Vec {} +impl Buffer for &mut [MaybeUninit] {} +impl Buffer for &mut [MaybeUninit; N] {} +#[cfg(feature = "alloc")] +impl Buffer for &mut Vec> {} +#[cfg(feature = "alloc")] +impl<'a, T> Buffer for SpareCapacity<'a, T> {} + +impl private::Sealed for &mut [T] { + type Output = usize; + + #[inline] + fn parts_mut(&mut self) -> (*mut T, usize) { + (self.as_mut_ptr(), self.len()) + } + + #[inline] + unsafe fn assume_init(self, len: usize) -> Self::Output { + len + } +} + +impl private::Sealed for &mut [T; N] { + type Output = usize; + + #[inline] + fn parts_mut(&mut self) -> (*mut T, usize) { + (self.as_mut_ptr(), N) + } + + #[inline] + unsafe fn assume_init(self, len: usize) -> Self::Output { + len + } +} + +// `Vec` implements `DerefMut` to `&mut [T]`, however it doesn't get +// auto-derefed in a `impl Buffer`, so we add this `impl` so that our users +// don't have to add an extra `*` in these situations. +#[cfg(feature = "alloc")] +impl private::Sealed for &mut Vec { + type Output = usize; + + #[inline] + fn parts_mut(&mut self) -> (*mut T, usize) { + (self.as_mut_ptr(), self.len()) + } + + #[inline] + unsafe fn assume_init(self, len: usize) -> Self::Output { + len + } +} + +impl<'a, T> private::Sealed for &'a mut [MaybeUninit] { + type Output = (&'a mut [T], &'a mut [MaybeUninit]); + + #[inline] + fn parts_mut(&mut self) -> (*mut T, usize) { + (self.as_mut_ptr().cast(), self.len()) + } + + #[inline] + unsafe fn assume_init(self, len: usize) -> Self::Output { + let (init, uninit) = self.split_at_mut(len); + + // SAFETY: The user asserts that the slice is now initialized. + let init = slice::from_raw_parts_mut(init.as_mut_ptr().cast::(), init.len()); + + (init, uninit) + } +} + +impl<'a, T, const N: usize> private::Sealed for &'a mut [MaybeUninit; N] { + type Output = (&'a mut [T], &'a mut [MaybeUninit]); + + #[inline] + fn parts_mut(&mut self) -> (*mut T, usize) { + (self.as_mut_ptr().cast(), self.len()) + } + + #[inline] + unsafe fn assume_init(self, len: usize) -> Self::Output { + let (init, uninit) = self.split_at_mut(len); + + // SAFETY: The user asserts that the slice is now initialized. + let init = slice::from_raw_parts_mut(init.as_mut_ptr().cast::(), init.len()); + + (init, uninit) + } +} + +#[cfg(feature = "alloc")] +impl<'a, T> private::Sealed for &'a mut Vec> { + type Output = (&'a mut [T], &'a mut [MaybeUninit]); + + #[inline] + fn parts_mut(&mut self) -> (*mut T, usize) { + (self.as_mut_ptr().cast(), self.len()) + } + + #[inline] + unsafe fn assume_init(self, len: usize) -> Self::Output { + let (init, uninit) = self.split_at_mut(len); + + // SAFETY: The user asserts that the slice is now initialized. + let init = slice::from_raw_parts_mut(init.as_mut_ptr().cast::(), init.len()); + + (init, uninit) + } +} + +/// A type that implements [`Buffer`] by appending to a `Vec`, up to its +/// capacity. +/// +/// To use this, use the [`spare_capacity`] function. +/// +/// Because this uses the capacity, and never reallocates, the `Vec` should +/// have some non-empty spare capacity. +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +pub struct SpareCapacity<'a, T>(&'a mut Vec); + +/// Construct an [`SpareCapacity`], which implements [`Buffer`]. +/// +/// This wraps a `Vec` and uses the spare capacity of the `Vec` as the buffer +/// to receive data in, automatically setting the length of the `Vec` to +/// include the received elements. +/// +/// This uses the existing capacity, and never allocates, so the `Vec` should +/// have some non-empty spare capacity! +/// +/// # Examples +/// +/// ``` +/// # fn test(input: rustix::fd::BorrowedFd) -> rustix::io::Result<()> { +/// use rustix::buffer::spare_capacity; +/// use rustix::io::{read, Errno}; +/// +/// let mut buf = Vec::with_capacity(1024); +/// match read(input, spare_capacity(&mut buf)) { +/// Ok(0) => { /* end of stream */ } +/// Ok(n) => { /* `buf` is now `n` bytes longer */ } +/// Err(Errno::INTR) => { /* `buf` is unmodified */ } +/// Err(e) => { +/// return Err(e); +/// } +/// } +/// +/// # Ok(()) +/// # } +/// ``` +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +pub fn spare_capacity<'a, T>(v: &'a mut Vec) -> SpareCapacity<'a, T> { + debug_assert_ne!( + v.capacity(), + 0, + "`extend` uses spare capacity, and never allocates new memory, so the `Vec` passed to it \ + should have some spare capacity." + ); + + SpareCapacity(v) +} + +#[cfg(feature = "alloc")] +impl<'a, T> private::Sealed for SpareCapacity<'a, T> { + /// The mutated `Vec` reflects the number of bytes read. We also return + /// this number, and a value of 0 indicates the end of the stream has + /// been reached. + type Output = usize; + + #[inline] + fn parts_mut(&mut self) -> (*mut T, usize) { + let spare = self.0.spare_capacity_mut(); + (spare.as_mut_ptr().cast(), spare.len()) + } + + #[inline] + unsafe fn assume_init(self, len: usize) -> Self::Output { + // We initialized `len` elements; extend the `Vec` to include them. + self.0.set_len(self.0.len() + len); + len + } +} + +mod private { + pub trait Sealed { + /// The result of the process operation. + type Output; + + /// Return a pointer and length for this buffer. + fn parts_mut(&mut self) -> (*mut T, usize); + + /// Convert a finished buffer pointer into its result. + /// + /// # Safety + /// + /// At least `len` bytes of the buffer must now be initialized. + unsafe fn assume_init(self, len: usize) -> Self::Output; + } } #[cfg(test)] mod tests { + #[allow(unused_imports)] use super::*; + #[cfg(not(windows))] #[test] - fn test_split_init() { - let mut input_array = [ - MaybeUninit::new(0_u8), - MaybeUninit::new(1_u8), - MaybeUninit::new(2_u8), - MaybeUninit::new(3_u8), + fn test_compilation() { + use crate::io::read; + use core::mem::MaybeUninit; + + let input = std::fs::File::open("Cargo.toml").unwrap(); + + let mut buf = vec![0_u8; 3]; + buf.reserve(32); + let _x: usize = read(&input, spare_capacity(&mut buf)).unwrap(); + let _x: (&mut [u8], &mut [MaybeUninit]) = + read(&input, buf.spare_capacity_mut()).unwrap(); + let _x: usize = read(&input, &mut buf).unwrap(); + let _x: usize = read(&input, &mut *buf).unwrap(); + let _x: usize = read(&input, &mut buf[..]).unwrap(); + let _x: usize = read(&input, &mut (*buf)[..]).unwrap(); + + let mut buf = [0, 0, 0]; + let _x: usize = read(&input, &mut buf).unwrap(); + let _x: usize = read(&input, &mut buf[..]).unwrap(); + + let mut buf = [ + MaybeUninit::uninit(), + MaybeUninit::uninit(), + MaybeUninit::uninit(), ]; - let input_array_clone = input_array.clone(); - let input_array_ptr = input_array.as_ptr(); - let output_array = [0_u8, 1_u8, 2_u8, 3_u8]; - - unsafe { - let (init, uninit) = split_init(&mut input_array, 0); - assert_eq!(init, &[]); - assert_eq!(uninit.len(), input_array_clone.len()); - assert_eq!(uninit.as_ptr(), input_array_ptr); - - let (init, uninit) = split_init(&mut input_array, input_array_clone.len()); - assert_eq!(init, &output_array[..]); - assert_eq!(init.as_ptr(), input_array_ptr.cast()); - assert_eq!(uninit.len(), 0); - assert_eq!( - uninit.as_ptr(), - input_array_ptr.add(input_array_clone.len()) - ); - - let (init, uninit) = split_init(&mut input_array, 2); - assert_eq!(init, &output_array[..2]); - assert_eq!(init.as_ptr(), input_array_ptr.cast()); - assert_eq!(uninit.len(), 2); - assert_eq!(uninit.as_ptr(), input_array_ptr.add(2)); + let _x: (&mut [u8], &mut [MaybeUninit]) = read(&input, &mut buf).unwrap(); + let _x: (&mut [u8], &mut [MaybeUninit]) = read(&input, &mut buf[..]).unwrap(); + + let mut buf = vec![ + MaybeUninit::uninit(), + MaybeUninit::uninit(), + MaybeUninit::uninit(), + ]; + let _x: (&mut [u8], &mut [MaybeUninit]) = read(&input, &mut buf).unwrap(); + let _x: (&mut [u8], &mut [MaybeUninit]) = read(&input, &mut buf[..]).unwrap(); + + // This is reduced from src/fs/inotify.rs line 177. + struct Wrapper<'a>(&'a mut [u8]); + impl<'a> Wrapper<'a> { + fn read(&mut self) { + let input = std::fs::File::open("Cargo.toml").unwrap(); + + // Ideally we'd write this. + //let _x: usize = read(&input, self.0).unwrap(); + // But we need to write this instead. + let _x: usize = read(&input, &mut *self.0).unwrap(); + } } + let mut buf = vec![0_u8; 3]; + let mut wrapper = Wrapper(&mut buf); + wrapper.read(); + + // Why does this get two error messages? + //let mut buf = [0, 0, 0]; + //let _x = read(&input, buf).unwrap(); } + #[cfg(not(windows))] #[test] - fn test_split_init_empty() { - unsafe { - let (init, uninit) = split_init(&mut [], 0); - assert!(init.is_empty()); - assert!(uninit.is_empty()); - } + fn test_slice() { + use crate::io::read; + use std::io::{Seek, SeekFrom}; + + let mut input = std::fs::File::open("Cargo.toml").unwrap(); + + let mut buf = [0_u8; 64]; + let nread = read(&input, &mut buf).unwrap(); + assert_eq!(nread, buf.len()); + assert_eq!(&buf[..9], b"[package]"); + input.seek(SeekFrom::End(-1)).unwrap(); + let nread = read(&input, &mut buf).unwrap(); + assert_eq!(nread, 1); + input.seek(SeekFrom::End(0)).unwrap(); + let nread = read(&input, &mut buf).unwrap(); + assert_eq!(nread, 0); + } + + #[cfg(not(windows))] + #[test] + fn test_slice_uninit() { + use crate::io::read; + use core::mem::MaybeUninit; + use std::io::{Seek, SeekFrom}; + + let mut input = std::fs::File::open("Cargo.toml").unwrap(); + + let mut buf = [MaybeUninit::::uninit(); 64]; + let (init, uninit) = read(&input, &mut buf).unwrap(); + assert_eq!(uninit.len(), 0); + assert_eq!(&init[..9], b"[package]"); + assert_eq!(init.len(), buf.len()); + assert_eq!( + unsafe { core::mem::transmute::<&mut [MaybeUninit], &mut [u8]>(&mut buf[..9]) }, + b"[package]" + ); + input.seek(SeekFrom::End(-1)).unwrap(); + let (init, uninit) = read(&input, &mut buf).unwrap(); + assert_eq!(init.len(), 1); + assert_eq!(uninit.len(), buf.len() - 1); + input.seek(SeekFrom::End(0)).unwrap(); + let (init, uninit) = read(&input, &mut buf).unwrap(); + assert_eq!(init.len(), 0); + assert_eq!(uninit.len(), buf.len()); + } + + #[cfg(not(windows))] + #[test] + fn test_spare_capacity() { + use crate::io::read; + use std::io::{Seek, SeekFrom}; + + let mut input = std::fs::File::open("Cargo.toml").unwrap(); + + let mut buf = Vec::with_capacity(64); + let nread = read(&input, spare_capacity(&mut buf)).unwrap(); + assert_eq!(nread, buf.capacity()); + assert_eq!(nread, buf.len()); + assert_eq!(&buf[..9], b"[package]"); + buf.clear(); + input.seek(SeekFrom::End(-1)).unwrap(); + let nread = read(&input, spare_capacity(&mut buf)).unwrap(); + assert_eq!(nread, 1); + assert_eq!(buf.len(), 1); + buf.clear(); + input.seek(SeekFrom::End(0)).unwrap(); + let nread = read(&input, spare_capacity(&mut buf)).unwrap(); + assert_eq!(nread, 0); + assert!(buf.is_empty()); } } diff --git a/src/event/epoll.rs b/src/event/epoll.rs index b0d142738..839f05de6 100644 --- a/src/event/epoll.rs +++ b/src/event/epoll.rs @@ -5,6 +5,7 @@ //! ```no_run //! # #[cfg(feature = "net")] //! # fn main() -> std::io::Result<()> { +//! use rustix::buffer::spare_capacity; //! use rustix::event::epoll; //! use rustix::fd::AsFd; //! use rustix::io::{ioctl_fionbio, read, write}; @@ -38,8 +39,8 @@ //! // Process events. //! let mut event_list = Vec::with_capacity(4); //! loop { -//! epoll::wait(&epoll, &mut event_list, None)?; -//! for event in &event_list { +//! epoll::wait(&epoll, spare_capacity(&mut event_list), None)?; +//! for event in event_list.drain(..) { //! let target = event.data; //! if target.u64() == 1 { //! // Accept a new connection, set it to non-blocking, and @@ -75,12 +76,10 @@ use super::epoll; pub use crate::backend::event::epoll::*; use crate::backend::event::syscalls; +use crate::buffer::Buffer; use crate::fd::{AsFd, OwnedFd}; use crate::io; -#[cfg(feature = "alloc")] use crate::timespec::Timespec; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; use core::ffi::c_void; use core::hash::{Hash, Hasher}; @@ -189,8 +188,7 @@ pub fn delete(epoll: EpollFd, source: SourceFd) - /// `epoll_wait(self, events, timeout)`—Waits for registered events of /// interest. /// -/// For each event of interest, an element is written to `events`. On -/// success, this returns the number of written elements. +/// For each event of interest, an element is written to `events`. /// /// Linux versions older than 5.11 (those that don't support `epoll_pwait2`) /// don't support timeouts greater than `c_int::MAX` milliseconds; if an @@ -204,24 +202,17 @@ pub fn delete(epoll: EpollFd, source: SourceFd) - /// /// [Linux]: https://man7.org/linux/man-pages/man2/epoll_wait.2.html /// [illumos]: https://www.illumos.org/man/3C/epoll_wait -#[cfg(feature = "alloc")] -#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] #[doc(alias = "epoll_wait")] #[inline] -pub fn wait( +pub fn wait>( epoll: EpollFd, - event_list: &mut Vec, + mut event_list: Buf, timeout: Option<&Timespec>, -) -> io::Result<()> { - // SAFETY: We're calling `epoll_wait` via FFI and we know how it - // behaves. - unsafe { - event_list.clear(); - let nfds = syscalls::epoll_wait(epoll.as_fd(), event_list.spare_capacity_mut(), timeout)?; - event_list.set_len(nfds); - } - - Ok(()) +) -> io::Result { + // SAFETY: `epoll_wait` behaves. + let nfds = unsafe { syscalls::epoll_wait(epoll.as_fd(), event_list.parts_mut(), timeout)? }; + // SAFETY: `epoll_wait` behaves. + unsafe { Ok(event_list.assume_init(nfds)) } } /// A record of an event that occurred. diff --git a/src/event/kqueue.rs b/src/event/kqueue.rs index 114719bf1..11dec9928 100644 --- a/src/event/kqueue.rs +++ b/src/event/kqueue.rs @@ -1,5 +1,6 @@ //! An API for interfacing with `kqueue`. +use crate::buffer::Buffer; use crate::fd::{AsFd, OwnedFd, RawFd}; use crate::pid::Pid; use crate::signal::Signal; @@ -8,9 +9,7 @@ use crate::{backend, io}; use backend::c::{self, intptr_t, kevent as kevent_t, uintptr_t}; use backend::event::syscalls; -use alloc::vec::Vec; use core::mem::zeroed; -use core::ptr::slice_from_raw_parts_mut; use core::time::Duration; /// A `kqueue` event for use with [`kevent`]. @@ -398,9 +397,6 @@ pub fn kqueue() -> io::Result { /// `kevent(kqueue, changelist, eventlist, timeout)`—Wait for events on a /// `kqueue`. /// -/// Note: in order to receive events, make sure to allocate capacity in the -/// eventlist! Otherwise, the function will return immediately. -/// /// # Safety /// /// The file descriptors referred to by the `Event` structs must be valid for @@ -418,32 +414,25 @@ pub fn kqueue() -> io::Result { /// [OpenBSD]: https://man.openbsd.org/kevent.2 /// [NetBSD]: https://man.netbsd.org/kevent.2 /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=kevent§ion=2 -pub unsafe fn kevent( +pub unsafe fn kevent>( kqueue: Fd, changelist: &[Event], - eventlist: &mut Vec, + mut eventlist: Buf, timeout: Option, -) -> io::Result { +) -> io::Result { let timeout = timeout.map(|timeout| backend::c::timespec { tv_sec: timeout.as_secs() as _, tv_nsec: timeout.subsec_nanos() as _, }); // Populate the event list with events. - eventlist.set_len(0); - let out_slice = slice_from_raw_parts_mut(eventlist.as_mut_ptr().cast(), eventlist.capacity()); - let res = syscalls::kevent( + let len = syscalls::kevent( kqueue.as_fd(), changelist, - &mut *out_slice, + eventlist.parts_mut(), timeout.as_ref(), ) - .map(|res| res as _); - - // Update the event list. - if let Ok(len) = res { - eventlist.set_len(len); - } + .map(|res| res as _)?; - res + Ok(eventlist.assume_init(len)) } diff --git a/src/fs/inotify.rs b/src/fs/inotify.rs index 8b83f25f2..eac4fbb82 100644 --- a/src/fs/inotify.rs +++ b/src/fs/inotify.rs @@ -47,7 +47,7 @@ use crate::backend::fs::syscalls; use crate::fd::{AsFd, OwnedFd}; use crate::ffi::CStr; use crate::io; -use crate::io::{read_uninit, Errno}; +use crate::io::{read, Errno}; use core::mem::{align_of, size_of, MaybeUninit}; use linux_raw_sys::general::inotify_event; @@ -175,7 +175,7 @@ impl<'buf, Fd: AsFd> Reader<'buf, Fd> { #[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> io::Result> { if self.is_buffer_empty() { - match read_uninit(self.fd.as_fd(), self.buf).map(|(init, _)| init.len()) { + match read(self.fd.as_fd(), &mut *self.buf).map(|(init, _)| init.len()) { Ok(0) => return Err(Errno::INVAL), Ok(bytes_read) => { self.initialized = bytes_read; diff --git a/src/fs/xattr.rs b/src/fs/xattr.rs index c6687d0f7..102307448 100644 --- a/src/fs/xattr.rs +++ b/src/fs/xattr.rs @@ -1,3 +1,6 @@ +#![allow(unsafe_code)] + +use crate::buffer::Buffer; use crate::{backend, ffi, io, path}; use backend::c; use backend::fd::AsFd; @@ -20,21 +23,25 @@ bitflags! { } } -/// `getxattr(path, name, value.as_ptr(), value.len())`—Get extended -/// filesystem attributes. +/// `getxattr(path, name, value)`—Get extended filesystem attributes. /// /// # References /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man2/getxattr.2.html #[inline] -pub fn getxattr( +pub fn getxattr>( path: P, name: Name, - value: &mut [u8], -) -> io::Result { + mut value: Buf, +) -> io::Result { path.into_with_c_str(|path| { - name.into_with_c_str(|name| backend::fs::syscalls::getxattr(path, name, value)) + name.into_with_c_str(|name| { + // SAFETY: `getxattr` behaves. + let len = unsafe { backend::fs::syscalls::getxattr(path, name, value.parts_mut())? }; + // SAFETY: `getxattr` behaves. + unsafe { Ok(value.assume_init(len)) } + }) }) } @@ -47,13 +54,18 @@ pub fn getxattr( /// /// [Linux]: https://man7.org/linux/man-pages/man2/lgetxattr.2.html #[inline] -pub fn lgetxattr( +pub fn lgetxattr>( path: P, name: Name, - value: &mut [u8], -) -> io::Result { + mut value: Buf, +) -> io::Result { path.into_with_c_str(|path| { - name.into_with_c_str(|name| backend::fs::syscalls::lgetxattr(path, name, value)) + name.into_with_c_str(|name| { + // SAFETY: `lgetxattr` behaves. + let len = unsafe { backend::fs::syscalls::lgetxattr(path, name, value.parts_mut())? }; + // SAFETY: `lgetxattr` behaves. + unsafe { Ok(value.assume_init(len)) } + }) }) } @@ -65,12 +77,17 @@ pub fn lgetxattr( /// /// [Linux]: https://man7.org/linux/man-pages/man2/fgetxattr.2.html #[inline] -pub fn fgetxattr( +pub fn fgetxattr>( fd: Fd, name: Name, - value: &mut [u8], -) -> io::Result { - name.into_with_c_str(|name| backend::fs::syscalls::fgetxattr(fd.as_fd(), name, value)) + mut value: Buf, +) -> io::Result { + name.into_with_c_str(|name| { + // SAFETY: `fgetxattr` behaves. + let len = unsafe { backend::fs::syscalls::fgetxattr(fd.as_fd(), name, value.parts_mut())? }; + // SAFETY: `fgetxattr` behaves. + unsafe { Ok(value.assume_init(len)) } + }) } /// `setxattr(path, name, value.as_ptr(), value.len(), flags)`—Set extended @@ -137,8 +154,13 @@ pub fn fsetxattr( /// /// [Linux]: https://man7.org/linux/man-pages/man2/listxattr.2.html #[inline] -pub fn listxattr(path: P, list: &mut [u8]) -> io::Result { - path.into_with_c_str(|path| backend::fs::syscalls::listxattr(path, list)) +pub fn listxattr>(path: P, mut list: Buf) -> io::Result { + path.into_with_c_str(|path| { + // SAFETY: `listxattr` behaves. + let len = unsafe { backend::fs::syscalls::listxattr(path, list.parts_mut())? }; + // SAFETY: `listxattr` behaves. + unsafe { Ok(list.assume_init(len)) } + }) } /// `llistxattr(path, list.as_ptr(), list.len())`—List extended filesystem @@ -149,8 +171,16 @@ pub fn listxattr(path: P, list: &mut [u8]) -> io::Result { /// /// [Linux]: https://man7.org/linux/man-pages/man2/llistxattr.2.html #[inline] -pub fn llistxattr(path: P, list: &mut [u8]) -> io::Result { - path.into_with_c_str(|path| backend::fs::syscalls::llistxattr(path, list)) +pub fn llistxattr>( + path: P, + mut list: Buf, +) -> io::Result { + path.into_with_c_str(|path| { + // SAFETY: `flistxattr` behaves. + let len = unsafe { backend::fs::syscalls::llistxattr(path, list.parts_mut())? }; + // SAFETY: `flistxattr` behaves. + unsafe { Ok(list.assume_init(len)) } + }) } /// `flistxattr(fd, list.as_ptr(), list.len())`—List extended filesystem @@ -161,8 +191,11 @@ pub fn llistxattr(path: P, list: &mut [u8]) -> io::Result { /// /// [Linux]: https://man7.org/linux/man-pages/man2/flistxattr.2.html #[inline] -pub fn flistxattr(fd: Fd, list: &mut [u8]) -> io::Result { - backend::fs::syscalls::flistxattr(fd.as_fd(), list) +pub fn flistxattr>(fd: Fd, mut list: Buf) -> io::Result { + // SAFETY: `flistxattr` behaves. + let len = unsafe { backend::fs::syscalls::flistxattr(fd.as_fd(), list.parts_mut())? }; + // SAFETY: `flistxattr` behaves. + unsafe { Ok(list.assume_init(len)) } } /// `removexattr(path, name)`—Remove an extended filesystem attribute. diff --git a/src/io/mod.rs b/src/io/mod.rs index ea08aada6..c048e2516 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -12,7 +12,6 @@ mod errno; #[cfg(not(windows))] mod fcntl; mod ioctl; -#[cfg(not(windows))] mod read_write; pub use close::*; @@ -22,5 +21,4 @@ pub use errno::{retry_on_intr, Errno, Result}; #[cfg(not(windows))] pub use fcntl::*; pub use ioctl::*; -#[cfg(not(windows))] pub use read_write::*; diff --git a/src/io/read_write.rs b/src/io/read_write.rs index fa587f5c4..ba5dd2912 100644 --- a/src/io/read_write.rs +++ b/src/io/read_write.rs @@ -2,10 +2,9 @@ #![allow(unsafe_code)] -use crate::buffer::split_init; +use crate::buffer::Buffer; use crate::{backend, io}; use backend::fd::AsFd; -use core::mem::MaybeUninit; // Declare `IoSlice` and `IoSliceMut`. #[cfg(not(windows))] @@ -16,9 +15,6 @@ pub use backend::io::types::ReadWriteFlags; /// `read(fd, buf)`—Reads from a stream. /// -/// This takes a `&mut [u8]` which Rust requires to contain initialized memory. -/// To use an uninitialized buffer, use [`read_uninit`]. -/// /// # References /// - [POSIX] /// - [Linux] @@ -40,27 +36,11 @@ pub use backend::io::types::ReadWriteFlags; /// [illumos]: https://illumos.org/man/2/read /// [glibc]: https://sourceware.org/glibc/manual/latest/html_node/I_002fO-Primitives.html#index-reading-from-a-file-descriptor #[inline] -pub fn read(fd: Fd, buf: &mut [u8]) -> io::Result { - unsafe { backend::io::syscalls::read(fd.as_fd(), buf.as_mut_ptr(), buf.len()) } -} - -/// `read(fd, buf)`—Reads from a stream. -/// -/// This is equivalent to [`read`], except that it can read into uninitialized -/// memory. It returns the slice that was initialized by this function and the -/// slice that remains uninitialized. -#[inline] -pub fn read_uninit( - fd: Fd, - buf: &mut [MaybeUninit], -) -> io::Result<(&mut [u8], &mut [MaybeUninit])> { - // Get number of initialized bytes. - let length = unsafe { - backend::io::syscalls::read(fd.as_fd(), buf.as_mut_ptr().cast::(), buf.len()) - }; - - // Split into the initialized and uninitialized portions. - Ok(unsafe { split_init(buf, length?) }) +pub fn read>(fd: Fd, mut buf: Buf) -> io::Result { + // SAFETY: `read` behaves. + let len = unsafe { backend::io::syscalls::read(fd.as_fd(), buf.parts_mut())? }; + // SAFETY: `read` behaves. + unsafe { Ok(buf.assume_init(len)) } } /// `write(fd, buf)`—Writes to a stream. @@ -92,9 +72,6 @@ pub fn write(fd: Fd, buf: &[u8]) -> io::Result { /// `pread(fd, buf, offset)`—Reads from a file at a given position. /// -/// This takes a `&mut [u8]` which Rust requires to contain initialized memory. -/// To use an uninitialized buffer, use [`pread_uninit`]. -/// /// # References /// - [POSIX] /// - [Linux] @@ -115,26 +92,17 @@ pub fn write(fd: Fd, buf: &[u8]) -> io::Result { /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=pread§ion=2 /// [illumos]: https://illumos.org/man/2/pread /// [glibc]: https://sourceware.org/glibc/manual/latest/html_node/I_002fO-Primitives.html#index-pread64 +#[cfg(not(windows))] #[inline] -pub fn pread(fd: Fd, buf: &mut [u8], offset: u64) -> io::Result { - unsafe { backend::io::syscalls::pread(fd.as_fd(), buf.as_mut_ptr(), buf.len(), offset) } -} - -/// `pread(fd, buf, offset)`—Reads from a file at a given position. -/// -/// This is equivalent to [`pread`], except that it can read into uninitialized -/// memory. It returns the slice that was initialized by this function and the -/// slice that remains uninitialized. -#[inline] -pub fn pread_uninit( +pub fn pread>( fd: Fd, - buf: &mut [MaybeUninit], + mut buf: Buf, offset: u64, -) -> io::Result<(&mut [u8], &mut [MaybeUninit])> { - let length = unsafe { - backend::io::syscalls::pread(fd.as_fd(), buf.as_mut_ptr().cast::(), buf.len(), offset) - }; - Ok(unsafe { split_init(buf, length?) }) +) -> io::Result { + // SAFETY: `pread` behaves. + let len = unsafe { backend::io::syscalls::pread(fd.as_fd(), buf.parts_mut(), offset)? }; + // SAFETY: `pread` behaves. + unsafe { Ok(buf.assume_init(len)) } } /// `pwrite(fd, bufs)`—Writes to a file at a given position. @@ -163,6 +131,7 @@ pub fn pread_uninit( /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=pwrite§ion=2 /// [illumos]: https://illumos.org/man/2/pwrite /// [glibc]: https://sourceware.org/glibc/manual/latest/html_node/I_002fO-Primitives.html#index-pwrite64 +#[cfg(not(windows))] #[inline] pub fn pwrite(fd: Fd, buf: &[u8], offset: u64) -> io::Result { backend::io::syscalls::pwrite(fd.as_fd(), buf, offset) @@ -190,7 +159,7 @@ pub fn pwrite(fd: Fd, buf: &[u8], offset: u64) -> io::Result { /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=readv§ion=2 /// [illumos]: https://illumos.org/man/2/readv /// [glibc]: https://sourceware.org/glibc/manual/latest/html_node/Scatter_002dGather.html#index-readv -#[cfg(not(any(target_os = "espidf", target_os = "horizon")))] +#[cfg(not(any(windows, target_os = "espidf", target_os = "horizon")))] #[inline] pub fn readv(fd: Fd, bufs: &mut [IoSliceMut<'_>]) -> io::Result { backend::io::syscalls::readv(fd.as_fd(), bufs) @@ -218,7 +187,7 @@ pub fn readv(fd: Fd, bufs: &mut [IoSliceMut<'_>]) -> io::Result /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=writev§ion=2 /// [illumos]: https://illumos.org/man/2/writev /// [glibc]: https://sourceware.org/glibc/manual/latest/html_node/Scatter_002dGather.html#index-writev -#[cfg(not(any(target_os = "espidf", target_os = "horizon")))] +#[cfg(not(any(windows, target_os = "espidf", target_os = "horizon")))] #[inline] pub fn writev(fd: Fd, bufs: &[IoSlice<'_>]) -> io::Result { backend::io::syscalls::writev(fd.as_fd(), bufs) @@ -244,6 +213,7 @@ pub fn writev(fd: Fd, bufs: &[IoSlice<'_>]) -> io::Result { /// [illumos]: https://illumos.org/man/2/preadv /// [glibc]: https://sourceware.org/glibc/manual/latest/html_node/Scatter_002dGather.html#index-preadv64 #[cfg(not(any( + windows, target_os = "espidf", target_os = "haiku", target_os = "horizon", @@ -281,6 +251,7 @@ pub fn preadv(fd: Fd, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io: /// [illumos]: https://illumos.org/man/2/pwritev /// [glibc]: https://sourceware.org/glibc/manual/latest/html_node/I_002fO-Primitives.html#index-pwrite64 #[cfg(not(any( + windows, target_os = "espidf", target_os = "haiku", target_os = "horizon", diff --git a/src/lib.rs b/src/lib.rs index 77c5f6dec..b76862431 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ //! # #[cfg(feature = "net")] //! # fn read(sock: std::net::TcpStream, buf: &mut [u8]) -> std::io::Result<()> { //! # use rustix::net::RecvFlags; -//! let nread: usize = rustix::net::recv(&sock, buf, RecvFlags::PEEK)?; +//! let (nread, _received) = rustix::net::recv(&sock, buf, RecvFlags::PEEK)?; //! # let _ = nread; //! # Ok(()) //! # } @@ -28,7 +28,7 @@ //! # #[cfg(windows)] //! # use std::os::windows::io::AsRawSocket; //! # const MSG_PEEK: i32 = libc::MSG_PEEK; -//! let nread: usize = unsafe { +//! let nread = unsafe { //! #[cfg(any(unix, target_os = "wasi"))] //! let raw = sock.as_raw_fd(); //! #[cfg(windows)] @@ -159,8 +159,7 @@ extern crate static_assertions; #[allow(unused_imports)] mod static_assertions; -// Internal utilities. -mod buffer; +pub mod buffer; #[cfg(not(windows))] #[macro_use] pub(crate) mod cstr; diff --git a/src/net/send_recv/mod.rs b/src/net/send_recv/mod.rs index 1d8a6a1b4..bbbb18088 100644 --- a/src/net/send_recv/mod.rs +++ b/src/net/send_recv/mod.rs @@ -2,13 +2,12 @@ #![allow(unsafe_code)] -use crate::buffer::split_init; +use crate::buffer::Buffer; use crate::net::addr::SocketAddrArg; use crate::net::SocketAddrAny; use crate::{backend, io}; use backend::fd::AsFd; use core::cmp::min; -use core::mem::MaybeUninit; pub use backend::net::send_recv::{RecvFlags, ReturnFlags, SendFlags}; @@ -20,8 +19,9 @@ pub use msg::*; /// `recv(fd, buf, flags)`—Reads data from a socket. /// -/// This takes a `&mut [u8]` which Rust requires to contain initialized memory. -/// To use an uninitialized buffer, use [`recv_uninit`]. +/// In addition to the `Buffer::Output` return value, this also returns the +/// number of bytes received before any truncation due to the +/// [`RecvFlags::TRUNC`] flag. /// /// # References /// - [Beej's Guide to Network Programming] @@ -48,31 +48,20 @@ pub use msg::*; /// [illumos]: https://illumos.org/man/3SOCKET/recv /// [glibc]: https://sourceware.org/glibc/manual/latest/html_node/Receiving-Data.html #[inline] -pub fn recv(fd: Fd, buf: &mut [u8], flags: RecvFlags) -> io::Result { - unsafe { backend::net::syscalls::recv(fd.as_fd(), buf.as_mut_ptr(), buf.len(), flags) } -} - -/// `recv(fd, buf, flags)`—Reads data from a socket. -/// -/// This is equivalent to [`recv`], except that it can read into uninitialized -/// memory. It returns the slice that was initialized by this function, the -/// slice that remains uninitialized, and the number of bytes received before -/// any truncation due to the `RecvFlags::TRUNC` flag. #[allow(clippy::type_complexity)] -#[inline] -pub fn recv_uninit( +pub fn recv>( fd: Fd, - buf: &mut [MaybeUninit], + mut buf: Buf, flags: RecvFlags, -) -> io::Result<(&mut [u8], &mut [MaybeUninit], usize)> { - let length = unsafe { - backend::net::syscalls::recv(fd.as_fd(), buf.as_mut_ptr().cast::(), buf.len(), flags)? - }; - +) -> io::Result<(Buf::Output, usize)> { + let (ptr, len) = buf.parts_mut(); + // SAFETY: `recv` behaves. + let recv_len = unsafe { backend::net::syscalls::recv(fd.as_fd(), (ptr, len), flags)? }; // If the `TRUNC` flag is set, the returned `length` may be longer than the // buffer length. - let (init, uninit) = unsafe { split_init(buf, min(length, buf.len())) }; - Ok((init, uninit, length)) + let min_len = min(len, recv_len); + // SAFETY: `recv` behaves. + unsafe { Ok((buf.assume_init(min_len), recv_len)) } } /// `send(fd, buf, flags)`—Writes data to a socket. @@ -109,8 +98,9 @@ pub fn send(fd: Fd, buf: &[u8], flags: SendFlags) -> io::Result /// `recvfrom(fd, buf, flags, addr, len)`—Reads data from a socket and /// returns the sender address. /// -/// This takes a `&mut [u8]` which Rust requires to contain initialized memory. -/// To use an uninitialized buffer, use [`recvfrom_uninit`]. +/// In addition to the `Buffer::Output` return value, this also returns the +/// number of bytes received before any truncation due to the +/// [`RecvFlags::TRUNC`] flag. /// /// # References /// - [Beej's Guide to Network Programming] @@ -137,47 +127,20 @@ pub fn send(fd: Fd, buf: &[u8], flags: SendFlags) -> io::Result /// [illumos]: https://illumos.org/man/3SOCKET/recvfrom /// [glibc]: https://sourceware.org/glibc/manual/latest/html_node/Receiving-Datagrams.html #[inline] -pub fn recvfrom( - fd: Fd, - buf: &mut [u8], - flags: RecvFlags, -) -> io::Result<(usize, Option)> { - unsafe { backend::net::syscalls::recvfrom(fd.as_fd(), buf.as_mut_ptr(), buf.len(), flags) } -} - -/// `recvfrom(fd, buf, flags, addr, len)`—Reads data from a socket and -/// returns the sender address. -/// -/// This is equivalent to [`recvfrom`], except that it can read into -/// uninitialized memory. It returns the slice that was initialized by this -/// function, the slice that remains uninitialized, the number of bytes -/// received before any truncation due to the `RecvFlags::TRUNC` flag, and -/// the address of the sender if known. -#[allow(clippy::type_complexity)] -#[inline] -pub fn recvfrom_uninit( +pub fn recvfrom>( fd: Fd, - buf: &mut [MaybeUninit], + mut buf: Buf, flags: RecvFlags, -) -> io::Result<( - &mut [u8], - &mut [MaybeUninit], - usize, - Option, -)> { - let (length, addr) = unsafe { - backend::net::syscalls::recvfrom( - fd.as_fd(), - buf.as_mut_ptr().cast::(), - buf.len(), - flags, - )? - }; - +) -> io::Result<(Buf::Output, usize, Option)> { + let (ptr, len) = buf.parts_mut(); + // SAFETY: `recvfrom` behaves. + let (recv_len, addr) = + unsafe { backend::net::syscalls::recvfrom(fd.as_fd(), (ptr, len), flags)? }; // If the `TRUNC` flag is set, the returned `length` may be longer than the // buffer length. - let (init, uninit) = unsafe { split_init(buf, min(length, buf.len())) }; - Ok((init, uninit, length, addr)) + let min_len = min(len, recv_len); + // SAFETY: `recvfrom` behaves. + unsafe { Ok((buf.assume_init(min_len), recv_len, addr)) } } /// `sendto(fd, buf, flags, addr)`—Writes data to a socket to a specific IP diff --git a/src/rand/getrandom.rs b/src/rand/getrandom.rs index ab838c40c..d912191c1 100644 --- a/src/rand/getrandom.rs +++ b/src/rand/getrandom.rs @@ -2,9 +2,8 @@ #![allow(unsafe_code)] -use crate::buffer::split_init; +use crate::buffer::Buffer; use crate::{backend, io}; -use core::mem::MaybeUninit; pub use backend::rand::types::GetRandomFlags; @@ -13,9 +12,6 @@ pub use backend::rand::types::GetRandomFlags; /// This is a very low-level API which may be difficult to use correctly. Most /// users should prefer to use [`getrandom`] or [`rand`] APIs instead. /// -/// This takes a `&mut [u8]` which Rust requires to contain initialized memory. -/// To use an uninitialized buffer, use [`getrandom_uninit`]. -/// /// [`getrandom`]: https://crates.io/crates/getrandom /// [`rand`]: https://crates.io/crates/rand /// @@ -24,25 +20,9 @@ pub use backend::rand::types::GetRandomFlags; /// /// [Linux]: https://man7.org/linux/man-pages/man2/getrandom.2.html #[inline] -pub fn getrandom(buf: &mut [u8], flags: GetRandomFlags) -> io::Result { - unsafe { backend::rand::syscalls::getrandom(buf.as_mut_ptr(), buf.len(), flags) } -} - -/// `getrandom(buf, flags)`—Reads a sequence of random bytes. -/// -/// This is identical to [`getrandom`], except that it can read into -/// uninitialized memory. It returns the slice that was initialized by this -/// function and the slice that remains uninitialized. -#[inline] -pub fn getrandom_uninit( - buf: &mut [MaybeUninit], - flags: GetRandomFlags, -) -> io::Result<(&mut [u8], &mut [MaybeUninit])> { - // Get number of initialized bytes. - let length = unsafe { - backend::rand::syscalls::getrandom(buf.as_mut_ptr().cast::(), buf.len(), flags) - }; - - // Split into the initialized and uninitialized portions. - Ok(unsafe { split_init(buf, length?) }) +pub fn getrandom>(mut buf: Buf, flags: GetRandomFlags) -> io::Result { + // SAFETY: `getrandom` behaves. + let len = unsafe { backend::rand::syscalls::getrandom(buf.parts_mut(), flags)? }; + // SAFETY: `getrandom` behaves. + unsafe { Ok(buf.assume_init(len)) } } diff --git a/src/rand/mod.rs b/src/rand/mod.rs index ec4c11221..e767c590d 100644 --- a/src/rand/mod.rs +++ b/src/rand/mod.rs @@ -4,4 +4,4 @@ mod getrandom; #[cfg(linux_kernel)] -pub use getrandom::{getrandom, getrandom_uninit, GetRandomFlags}; +pub use getrandom::{getrandom, GetRandomFlags}; diff --git a/tests/event/epoll.rs b/tests/event/epoll.rs index 93e3db93c..3d5e963d7 100644 --- a/tests/event/epoll.rs +++ b/tests/event/epoll.rs @@ -1,3 +1,4 @@ +use rustix::buffer::spare_capacity; use rustix::event::epoll; use rustix::io::{ioctl_fionbio, read, write}; use rustix::net::{ @@ -40,8 +41,8 @@ fn server(ready: Arc<(Mutex, Condvar)>) -> ! { let mut event_list = Vec::with_capacity(4); loop { - epoll::wait(&epoll, &mut event_list, None).unwrap(); - for event in &event_list { + epoll::wait(&epoll, spare_capacity(&mut event_list), None).unwrap(); + for event in event_list.drain(..) { let target = event.data; if target.u64() == 1 { let conn_sock = accept(&listen_sock).unwrap(); diff --git a/tests/event/epoll_timeout.rs b/tests/event/epoll_timeout.rs index b02c49816..cf915b005 100644 --- a/tests/event/epoll_timeout.rs +++ b/tests/event/epoll_timeout.rs @@ -1,3 +1,4 @@ +use rustix::buffer::spare_capacity; use rustix::event::{epoll, Timespec}; use std::time::Instant; @@ -9,7 +10,7 @@ fn epoll_timeout() { let mut events = Vec::with_capacity(1); epoll::wait( &epoll_fd, - &mut events, + spare_capacity(&mut events), Some(&Timespec { tv_sec: 0, tv_nsec: 1_000_000, diff --git a/tests/event/select.rs b/tests/event/select.rs index d83706c77..6552a6882 100644 --- a/tests/event/select.rs +++ b/tests/event/select.rs @@ -245,7 +245,7 @@ fn test_select_with_sockets() { let mut buf = [b'\0']; assert_eq!( retry_on_intr(|| recv(&reader, &mut buf, RecvFlags::empty())).unwrap(), - 1 + (1, 1) ); assert_eq!(buf[0], b'a'); @@ -364,7 +364,7 @@ fn test_select_with_maxfd_sockets() { let mut buf = [b'\0']; assert_eq!( retry_on_intr(|| recv(&reader, &mut buf, RecvFlags::empty())).unwrap(), - 1 + (1, 1) ); assert_eq!(buf[0], b'a'); diff --git a/tests/fs/xattr.rs b/tests/fs/xattr.rs index 6e0a6c8e8..936ec7437 100644 --- a/tests/fs/xattr.rs +++ b/tests/fs/xattr.rs @@ -10,14 +10,16 @@ fn xattr_basic() { #[cfg(apple)] let enodata = libc::ENOATTR; + let mut empty: [u8; 0] = []; + assert_eq!( - rustix::fs::getxattr("/no/such/path", "user.test", &mut []) + rustix::fs::getxattr("/no/such/path", "user.test", &mut empty) .unwrap_err() .kind(), io::ErrorKind::NotFound ); assert_eq!( - rustix::fs::lgetxattr("/no/such/path", "user.test", &mut []) + rustix::fs::lgetxattr("/no/such/path", "user.test", &mut empty) .unwrap_err() .kind(), io::ErrorKind::NotFound @@ -35,13 +37,13 @@ fn xattr_basic() { io::ErrorKind::NotFound ); assert_eq!( - rustix::fs::listxattr("/no/such/path", &mut []) + rustix::fs::listxattr("/no/such/path", &mut empty) .unwrap_err() .kind(), io::ErrorKind::NotFound ); assert_eq!( - rustix::fs::llistxattr("/no/such/path", &mut []) + rustix::fs::llistxattr("/no/such/path", &mut empty) .unwrap_err() .kind(), io::ErrorKind::NotFound @@ -60,13 +62,13 @@ fn xattr_basic() { ); assert_eq!( - rustix::fs::getxattr("Cargo.toml", "user.test", &mut []) + rustix::fs::getxattr("Cargo.toml", "user.test", &mut empty) .unwrap_err() .raw_os_error(), enodata ); assert_eq!( - rustix::fs::lgetxattr("Cargo.toml", "user.test", &mut []) + rustix::fs::lgetxattr("Cargo.toml", "user.test", &mut empty) .unwrap_err() .raw_os_error(), enodata @@ -83,8 +85,8 @@ fn xattr_basic() { .raw_os_error(), enodata ); - assert_eq!(rustix::fs::listxattr("Cargo.toml", &mut []).unwrap(), 0); - assert_eq!(rustix::fs::llistxattr("Cargo.toml", &mut []).unwrap(), 0); + assert_eq!(rustix::fs::listxattr("Cargo.toml", &mut empty).unwrap(), 0); + assert_eq!(rustix::fs::llistxattr("Cargo.toml", &mut empty).unwrap(), 0); assert_eq!( rustix::fs::removexattr("Cargo.toml", "user.test") .unwrap_err() @@ -100,7 +102,7 @@ fn xattr_basic() { let file = std::fs::File::open("Cargo.toml").unwrap(); assert_eq!( - rustix::fs::fgetxattr(&file, "user.test", &mut []) + rustix::fs::fgetxattr(&file, "user.test", &mut empty) .unwrap_err() .raw_os_error(), enodata @@ -111,7 +113,7 @@ fn xattr_basic() { .raw_os_error(), enodata ); - assert_eq!(rustix::fs::flistxattr(&file, &mut []).unwrap(), 0); + assert_eq!(rustix::fs::flistxattr(&file, &mut empty).unwrap(), 0); assert_eq!( rustix::fs::fremovexattr(&file, "user.test") .unwrap_err() diff --git a/tests/io/read_write.rs b/tests/io/read_write.rs index 840660f69..4ddd89164 100644 --- a/tests/io/read_write.rs +++ b/tests/io/read_write.rs @@ -73,7 +73,7 @@ fn test_readwrite_p() { fn test_readwrite_p_uninit() { use core::mem::MaybeUninit; use rustix::fs::{openat, Mode, OFlags, CWD}; - use rustix::io::{pread_uninit, pwrite}; + use rustix::io::{pread, pwrite}; let tmp = tempfile::tempdir().unwrap(); let dir = openat(CWD, tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); @@ -88,9 +88,9 @@ fn test_readwrite_p_uninit() { pwrite(&file, b"hello", 200).unwrap(); pwrite(&file, b"world", 300).unwrap(); let mut buf = [MaybeUninit::uninit(); 5]; - let (init, _) = pread_uninit(&file, &mut buf, 200).unwrap(); + let (init, _) = pread(&file, &mut buf, 200).unwrap(); assert_eq!(&init, b"hello"); - let (init, _) = pread_uninit(&file, &mut buf, 300).unwrap(); + let (init, _) = pread(&file, &mut buf, 300).unwrap(); assert_eq!(&init, b"world"); } @@ -152,7 +152,7 @@ fn test_readwrite() { fn test_readwrite_uninit() { use core::mem::MaybeUninit; use rustix::fs::{openat, seek, Mode, OFlags, SeekFrom, CWD}; - use rustix::io::{read_uninit, write}; + use rustix::io::{read, write}; let tmp = tempfile::tempdir().unwrap(); let dir = openat(CWD, tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); @@ -168,9 +168,9 @@ fn test_readwrite_uninit() { write(&file, b"world").unwrap(); seek(&file, SeekFrom::Start(0)).unwrap(); let mut buf = [MaybeUninit::uninit(); 5]; - let (init, _) = read_uninit(&file, &mut buf).unwrap(); + let (init, _) = read(&file, &mut buf).unwrap(); assert_eq!(&init, b"hello"); - let (init, _) = read_uninit(&file, &mut buf).unwrap(); + let (init, _) = read(&file, &mut buf).unwrap(); assert_eq!(&init, b"world"); } diff --git a/tests/net/connect_bind_send.rs b/tests/net/connect_bind_send.rs index 67cfe5030..c60237059 100644 --- a/tests/net/connect_bind_send.rs +++ b/tests/net/connect_bind_send.rs @@ -30,10 +30,12 @@ fn net_v4_connect_any() { let accepted = rustix::net::accept(&listener).expect("accept"); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -63,10 +65,12 @@ fn net_v4_connect_any_accept_with() { let accepted = rustix::net::accept_with(&listener, SocketFlags::CLOEXEC).expect("accept_with"); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -95,10 +99,12 @@ fn net_v6_connect_any() { let accepted = rustix::net::accept(&listener).expect("accept"); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -128,10 +134,12 @@ fn net_v6_connect_any_accept_with() { let accepted = rustix::net::accept_with(&listener, SocketFlags::CLOEXEC).expect("accept_with"); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -160,10 +168,12 @@ fn net_v4_connect() { let accepted = rustix::net::accept(&listener).expect("accept"); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -195,10 +205,12 @@ fn net_v6_connect() { let accepted = rustix::net::accept(&listener).expect("accept"); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -310,10 +322,12 @@ fn net_v4_bind_any() { let accepted = rustix::net::accept(&listener).expect("accept"); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -341,10 +355,12 @@ fn net_v6_bind_any() { let accepted = rustix::net::accept(&listener).expect("accept"); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -373,11 +389,12 @@ fn net_v4_sendto() { let accepted = rustix::net::accept(&listener).expect("accept"); let mut response = [0_u8; 128]; - let (n, from) = + let (n, actual, from) = rustix::net::recvfrom(&accepted, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); assert!(from.is_none()); @@ -407,11 +424,12 @@ fn net_v6_sendto() { let accepted = rustix::net::accept(&listener).expect("accept"); let mut response = [0_u8; 128]; - let (n, from) = + let (n, actual, from) = rustix::net::recvfrom(&accepted, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); assert!(from.is_none()); @@ -440,11 +458,12 @@ fn net_v4_sendto_any() { let accepted = rustix::net::accept(&listener).expect("accept"); let mut response = [0_u8; 128]; - let (n, from) = + let (n, actual, from) = rustix::net::recvfrom(&accepted, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); assert!(from.is_none()); @@ -473,11 +492,12 @@ fn net_v6_sendto_any() { let accepted = rustix::net::accept(&listener).expect("accept"); let mut response = [0_u8; 128]; - let (n, from) = + let (n, actual, from) = rustix::net::recvfrom(&accepted, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); assert!(from.is_none()); @@ -522,10 +542,12 @@ fn net_v4_acceptfrom() { assert_eq!(from.clone().port(), peer_addr.port()); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -569,10 +591,12 @@ fn net_v6_acceptfrom() { assert_eq!(from.clone().port(), peer_addr.port()); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -596,8 +620,10 @@ fn net_shutdown() { let accepted = rustix::net::accept(&listener).expect("accept"); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&accepted, &mut response, RecvFlags::empty()).expect("recv"); assert_eq!(n, 0); + assert_eq!(actual, 0); drop(sender); } diff --git a/tests/net/dgram.rs b/tests/net/dgram.rs index d4674e82f..18da4ebb5 100644 --- a/tests/net/dgram.rs +++ b/tests/net/dgram.rs @@ -28,10 +28,12 @@ fn net_dgram_v4_connect_any() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -59,10 +61,12 @@ fn net_dgram_v4_connect_any_accept_with() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -89,10 +93,12 @@ fn net_dgram_v6_connect_any() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -120,10 +126,12 @@ fn net_dgram_v6_connect_any_accept_with() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -150,10 +158,12 @@ fn net_dgram_v4_connect() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -180,10 +190,12 @@ fn net_dgram_v6_connect() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -293,10 +305,12 @@ fn net_dgram_v4_bind_any() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -322,10 +336,12 @@ fn net_dgram_v6_bind_any() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -353,11 +369,12 @@ fn net_dgram_v4_connect_sendto() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let (n, from) = + let (n, actual, from) = rustix::net::recvfrom(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); @@ -388,11 +405,12 @@ fn net_dgram_v4_sendto() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let (n, from) = + let (n, actual, from) = rustix::net::recvfrom(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); @@ -425,11 +443,12 @@ fn net_dgram_v6_connect_sendto() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let (n, from) = + let (n, actual, from) = rustix::net::recvfrom(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); @@ -460,11 +479,12 @@ fn net_dgram_v6_sendto() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let (n, from) = + let (n, actual, from) = rustix::net::recvfrom(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); @@ -496,11 +516,12 @@ fn net_dgram_v4_connect_sendto_any() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let (n, from) = + let (n, actual, from) = rustix::net::recvfrom(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); @@ -531,11 +552,12 @@ fn net_dgram_v4_sendto_any() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let (n, from) = + let (n, actual, from) = rustix::net::recvfrom(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); @@ -568,11 +590,12 @@ fn net_dgram_v6_connect_sendto_any() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let (n, from) = + let (n, actual, from) = rustix::net::recvfrom(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); @@ -604,11 +627,12 @@ fn net_dgram_v6_sendto_any() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let (n, from) = + let (n, actual, from) = rustix::net::recvfrom(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); @@ -641,10 +665,12 @@ fn net_dgram_v4_acceptfrom() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } @@ -670,10 +696,12 @@ fn net_dgram_v6_acceptfrom() { assert_eq!(n, request.len()); let mut response = [0_u8; 128]; - let n = rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); + let (n, actual) = + rustix::net::recv(&listener, &mut response, RecvFlags::empty()).expect("recv"); // Not strictly required, but it makes the test simpler. assert_eq!(n, request.len()); + assert_eq!(actual, request.len()); assert_eq!(request, &response[..n]); } diff --git a/tests/net/netlink.rs b/tests/net/netlink.rs index c9bfe3658..9c891c2f0 100644 --- a/tests/net/netlink.rs +++ b/tests/net/netlink.rs @@ -56,9 +56,10 @@ fn test_usersock() { sendto(client, data, SendFlags::empty(), &addr).unwrap(); let mut buffer = [0_u8; 4096]; - let (len, src) = recvfrom(&server, &mut buffer, RecvFlags::empty()).unwrap(); + let (len, actual, src) = recvfrom(&server, &mut buffer, RecvFlags::empty()).unwrap(); assert_eq!(&buffer[..len], data); + assert_eq!(len, actual); let src = SocketAddrNetlink::try_from(src.unwrap()).unwrap(); assert_eq!(src.groups(), 0); } diff --git a/tests/net/poll.rs b/tests/net/poll.rs index 7397a4949..c52015c0a 100644 --- a/tests/net/poll.rs +++ b/tests/net/poll.rs @@ -41,9 +41,10 @@ fn server(ready: Arc<(Mutex, Condvar)>) { #[cfg(not(target_os = "espidf"))] { let expected_nread = rustix::io::ioctl_fionread(&data_socket).unwrap(); - let nread = recv(&data_socket, &mut buffer, RecvFlags::empty()).unwrap(); + let (nread, actual) = recv(&data_socket, &mut buffer, RecvFlags::empty()).unwrap(); assert_eq!(String::from_utf8_lossy(&buffer[..nread]), "hello, world"); assert_eq!(expected_nread, nread as u64); + assert_eq!(expected_nread, actual as u64); } let mut fds = [PollFd::new(&data_socket, PollFlags::OUT)]; @@ -85,9 +86,10 @@ fn client(ready: Arc<(Mutex, Condvar)>) { #[cfg(not(target_os = "espidf"))] { let expected_nread = rustix::io::ioctl_fionread(&data_socket).unwrap(); - let nread = recv(&data_socket, &mut buffer, RecvFlags::empty()).unwrap(); + let (nread, actual) = recv(&data_socket, &mut buffer, RecvFlags::empty()).unwrap(); assert_eq!(String::from_utf8_lossy(&buffer[..nread]), "goodnight, moon"); assert_eq!(expected_nread, nread as u64); + assert_eq!(expected_nread, actual as u64); } } diff --git a/tests/net/recv_trunc.rs b/tests/net/recv_trunc.rs index 751f8c836..b62b8ba15 100644 --- a/tests/net/recv_trunc.rs +++ b/tests/net/recv_trunc.rs @@ -5,7 +5,7 @@ use rustix::net::ReturnFlags; use rustix::net::{AddressFamily, RecvFlags, SendFlags, SocketAddrUnix, SocketType}; use std::mem::MaybeUninit; -/// Test `recv_uninit` with the `RecvFlags::Trunc` flag. +/// Test `recv` with `&mut [MaybeUninit]` with the `RecvFlags::Trunc` flag. #[test] fn net_recv_uninit_trunc() { crate::init(); @@ -27,9 +27,8 @@ fn net_recv_uninit_trunc() { #[cfg(not(any(apple, solarish, target_os = "netbsd")))] { let mut response = [MaybeUninit::::zeroed(); 5]; - let (init, uninit, length) = - rustix::net::recv_uninit(&receiver, &mut response, RecvFlags::TRUNC) - .expect("recv_uninit"); + let ((init, uninit), length) = + rustix::net::recv(&receiver, &mut response, RecvFlags::TRUNC).expect("recv_uninit"); // We used the `TRUNC` flag, so we should have only gotten 5 bytes. assert_eq!(init, b"Hello"); @@ -45,9 +44,8 @@ fn net_recv_uninit_trunc() { // This time receive it without `TRUNC`. This should fail. let mut response = [MaybeUninit::::zeroed(); 5]; - let (init, uninit, length) = - rustix::net::recv_uninit(&receiver, &mut response, RecvFlags::empty()) - .expect("recv_uninit"); + let ((init, uninit), length) = + rustix::net::recv(&receiver, &mut response, RecvFlags::empty()).expect("recv_uninit"); // We didn't use the `TRUNC` flag, so we should have received 15 bytes, // truncated to 5 bytes. diff --git a/tests/net/v4.rs b/tests/net/v4.rs index 129468d41..bb11a5293 100644 --- a/tests/net/v4.rs +++ b/tests/net/v4.rs @@ -35,8 +35,9 @@ fn server(ready: Arc<(Mutex, Condvar)>) { let mut buffer = vec![0; BUFFER_SIZE]; let data_socket = accept(&connection_socket).unwrap(); - let nread = recv(&data_socket, &mut buffer, RecvFlags::empty()).unwrap(); + let (nread, actual) = recv(&data_socket, &mut buffer, RecvFlags::empty()).unwrap(); assert_eq!(String::from_utf8_lossy(&buffer[..nread]), "hello, world"); + assert_eq!(actual, nread); send(&data_socket, b"goodnight, moon", SendFlags::empty()).unwrap(); } @@ -59,8 +60,9 @@ fn client(ready: Arc<(Mutex, Condvar)>) { send(&data_socket, b"hello, world", SendFlags::empty()).unwrap(); - let nread = recv(&data_socket, &mut buffer, RecvFlags::empty()).unwrap(); + let (nread, actual) = recv(&data_socket, &mut buffer, RecvFlags::empty()).unwrap(); assert_eq!(String::from_utf8_lossy(&buffer[..nread]), "goodnight, moon"); + assert_eq!(actual, nread); } #[test] diff --git a/tests/net/v6.rs b/tests/net/v6.rs index 9bcba69c5..4016c18ad 100644 --- a/tests/net/v6.rs +++ b/tests/net/v6.rs @@ -35,8 +35,9 @@ fn server(ready: Arc<(Mutex, Condvar)>) { let mut buffer = vec![0; BUFFER_SIZE]; let data_socket = accept(&connection_socket).unwrap(); - let nread = recv(&data_socket, &mut buffer, RecvFlags::empty()).unwrap(); + let (nread, actual) = recv(&data_socket, &mut buffer, RecvFlags::empty()).unwrap(); assert_eq!(String::from_utf8_lossy(&buffer[..nread]), "hello, world"); + assert_eq!(actual, nread); send(&data_socket, b"goodnight, moon", SendFlags::empty()).unwrap(); } @@ -59,8 +60,9 @@ fn client(ready: Arc<(Mutex, Condvar)>) { send(&data_socket, b"hello, world", SendFlags::empty()).unwrap(); - let nread = recv(&data_socket, &mut buffer, RecvFlags::empty()).unwrap(); + let (nread, actual) = recv(&data_socket, &mut buffer, RecvFlags::empty()).unwrap(); assert_eq!(String::from_utf8_lossy(&buffer[..nread]), "goodnight, moon"); + assert_eq!(actual, nread); } #[test] diff --git a/tests/rand/getrandom.rs b/tests/rand/getrandom.rs index 9ae39f1fb..403b08205 100644 --- a/tests/rand/getrandom.rs +++ b/tests/rand/getrandom.rs @@ -1,5 +1,5 @@ use core::mem::MaybeUninit; -use rustix::rand::{getrandom, getrandom_uninit, GetRandomFlags}; +use rustix::rand::{getrandom, GetRandomFlags}; #[test] fn test_getrandom() { @@ -10,7 +10,7 @@ fn test_getrandom() { #[test] fn test_getrandom_uninit() { let mut buf = unsafe { MaybeUninit::<[MaybeUninit; 256]>::uninit().assume_init() }; - let (init, uninit) = getrandom_uninit(&mut buf, GetRandomFlags::empty()).unwrap(); + let (init, uninit) = getrandom(&mut buf, GetRandomFlags::empty()).unwrap(); let combined_len = init.len() + uninit.len(); assert_eq!(buf.len(), combined_len); }