diff --git a/src/backend/libc/io/syscalls.rs b/src/backend/libc/io/syscalls.rs index 8d5aefadc..e28e6befc 100644 --- a/src/backend/libc/io/syscalls.rs +++ b/src/backend/libc/io/syscalls.rs @@ -26,14 +26,8 @@ use { crate::io::{IoSlice, IoSliceMut}, }; -pub(crate) fn read(fd: BorrowedFd<'_>, buf: &mut [u8]) -> io::Result { - unsafe { - ret_usize(c::read( - borrowed_fd(fd), - buf.as_mut_ptr().cast(), - min(buf.len(), READ_LIMIT), - )) - } +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) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result { @@ -46,8 +40,13 @@ pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result { } } -pub(crate) fn pread(fd: BorrowedFd<'_>, buf: &mut [u8], offset: u64) -> io::Result { - let len = min(buf.len(), READ_LIMIT); +pub(crate) unsafe fn pread( + fd: BorrowedFd<'_>, + buf: *mut u8, + len: usize, + offset: u64, +) -> io::Result { + let len = min(len, READ_LIMIT); // Silently cast; we'll get `EINVAL` if the value is negative. let offset = offset as i64; @@ -56,14 +55,7 @@ pub(crate) fn pread(fd: BorrowedFd<'_>, buf: &mut [u8], offset: u64) -> io::Resu #[cfg(any(target_os = "espidf", target_os = "vita"))] let offset: i32 = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?; - unsafe { - ret_usize(c::pread( - borrowed_fd(fd), - buf.as_mut_ptr().cast(), - len, - offset, - )) - } + ret_usize(c::pread(borrowed_fd(fd), buf.cast(), len, offset)) } pub(crate) fn pwrite(fd: BorrowedFd<'_>, buf: &[u8], offset: u64) -> io::Result { diff --git a/src/backend/libc/net/syscalls.rs b/src/backend/libc/net/syscalls.rs index 97b862033..45efa7d6a 100644 --- a/src/backend/libc/net/syscalls.rs +++ b/src/backend/libc/net/syscalls.rs @@ -31,15 +31,18 @@ use { }; #[cfg(not(any(target_os = "redox", target_os = "wasi")))] -pub(crate) fn recv(fd: BorrowedFd<'_>, buf: &mut [u8], flags: RecvFlags) -> io::Result { - unsafe { - ret_send_recv(c::recv( - borrowed_fd(fd), - buf.as_mut_ptr().cast(), - send_recv_len(buf.len()), - bitflags_bits!(flags), - )) - } +pub(crate) unsafe fn recv( + fd: BorrowedFd<'_>, + buf: *mut u8, + len: usize, + flags: RecvFlags, +) -> io::Result { + ret_send_recv(c::recv( + borrowed_fd(fd), + buf.cast(), + send_recv_len(len), + bitflags_bits!(flags), + )) } #[cfg(not(any(target_os = "redox", target_os = "wasi")))] @@ -55,35 +58,34 @@ pub(crate) fn send(fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags) -> io::Resu } #[cfg(not(any(target_os = "redox", target_os = "wasi")))] -pub(crate) fn recvfrom( +pub(crate) unsafe fn recvfrom( fd: BorrowedFd<'_>, - buf: &mut [u8], + buf: *mut u8, + buf_len: usize, flags: RecvFlags, ) -> io::Result<(usize, Option)> { - unsafe { - let mut storage = MaybeUninit::::uninit(); - let mut len = size_of::() as c::socklen_t; - - // `recvfrom` does not write to the storage if the socket is - // connection-oriented sockets, so we initialize the family field to - // `AF_UNSPEC` so that we can detect this case. - initialize_family_to_unspec(storage.as_mut_ptr()); - - ret_send_recv(c::recvfrom( - borrowed_fd(fd), - buf.as_mut_ptr().cast(), - send_recv_len(buf.len()), - bitflags_bits!(flags), - storage.as_mut_ptr().cast(), - &mut len, - )) - .map(|nread| { - ( - nread, - maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()), - ) - }) - } + let mut storage = MaybeUninit::::uninit(); + let mut len = size_of::() as c::socklen_t; + + // `recvfrom` does not write to the storage if the socket is + // connection-oriented sockets, so we initialize the family field to + // `AF_UNSPEC` so that we can detect this case. + initialize_family_to_unspec(storage.as_mut_ptr()); + + ret_send_recv(c::recvfrom( + borrowed_fd(fd), + buf.cast(), + send_recv_len(buf_len), + bitflags_bits!(flags), + storage.as_mut_ptr().cast(), + &mut len, + )) + .map(|nread| { + ( + nread, + maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()), + ) + }) } #[cfg(not(any(target_os = "redox", target_os = "wasi")))] diff --git a/src/backend/libc/rand/syscalls.rs b/src/backend/libc/rand/syscalls.rs index 8a995f2e6..3a3929e7d 100644 --- a/src/backend/libc/rand/syscalls.rs +++ b/src/backend/libc/rand/syscalls.rs @@ -4,11 +4,15 @@ use {crate::backend::c, crate::backend::conv::ret_usize, crate::io, crate::rand::GetRandomFlags}; #[cfg(linux_kernel)] -pub(crate) fn getrandom(buf: &mut [u8], flags: GetRandomFlags) -> io::Result { +pub(crate) unsafe fn getrandom( + buf: *mut u8, + cap: 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 } - unsafe { ret_usize(getrandom(buf.as_mut_ptr().cast(), buf.len(), flags.bits())) } + ret_usize(getrandom(buf.cast(), cap, flags.bits())) } diff --git a/src/backend/linux_raw/io/syscalls.rs b/src/backend/linux_raw/io/syscalls.rs index 62c68a22f..c38f28fce 100644 --- a/src/backend/linux_raw/io/syscalls.rs +++ b/src/backend/linux_raw/io/syscalls.rs @@ -15,7 +15,7 @@ use crate::backend::conv::loff_t_from_u64; use crate::backend::conv::zero; use crate::backend::conv::{ c_uint, pass_usize, raw_fd, ret, ret_c_int, ret_c_uint, ret_discarded_fd, ret_owned_fd, - ret_usize, slice, slice_mut, + ret_usize, slice, }; #[cfg(target_pointer_width = "32")] use crate::backend::conv::{hi, lo}; @@ -29,27 +29,28 @@ use core::cmp; use linux_raw_sys::general::{F_DUPFD_CLOEXEC, F_GETFD, F_SETFD}; #[inline] -pub(crate) fn read(fd: BorrowedFd<'_>, buf: &mut [u8]) -> io::Result { - let (buf_addr_mut, buf_len) = slice_mut(buf); - - unsafe { ret_usize(syscall!(__NR_read, fd, buf_addr_mut, buf_len)) } +pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: *mut u8, len: usize) -> io::Result { + ret_usize(syscall!(__NR_read, fd, buf, pass_usize(len))) } #[inline] -pub(crate) fn pread(fd: BorrowedFd<'_>, buf: &mut [u8], pos: u64) -> io::Result { - let (buf_addr_mut, buf_len) = slice_mut(buf); - +pub(crate) unsafe fn pread( + fd: BorrowedFd<'_>, + buf: *mut u8, + len: usize, + pos: u64, +) -> io::Result { // #[cfg(all( target_pointer_width = "32", any(target_arch = "arm", target_arch = "mips", target_arch = "mips32r6"), ))] - unsafe { + { ret_usize(syscall!( __NR_pread64, fd, - buf_addr_mut, - buf_len, + buf, + pass_usize(len), zero(), hi(pos), lo(pos) @@ -59,26 +60,24 @@ pub(crate) fn pread(fd: BorrowedFd<'_>, buf: &mut [u8], pos: u64) -> io::Result< target_pointer_width = "32", not(any(target_arch = "arm", target_arch = "mips", target_arch = "mips32r6")), ))] - unsafe { + { ret_usize(syscall!( __NR_pread64, fd, - buf_addr_mut, - buf_len, + buf, + pass_usize(len), hi(pos), lo(pos) )) } #[cfg(target_pointer_width = "64")] - unsafe { - ret_usize(syscall!( - __NR_pread64, - fd, - buf_addr_mut, - buf_len, - loff_t_from_u64(pos) - )) - } + ret_usize(syscall!( + __NR_pread64, + fd, + buf, + pass_usize(len), + loff_t_from_u64(pos) + )) } #[inline] @@ -268,15 +267,15 @@ pub(crate) fn is_read_write(fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> { // Do a `recv` with `PEEK` and `DONTWAIT` for 1 byte. A 0 indicates // the read side is shut down; an `EWOULDBLOCK` indicates the read // side is still open. - // - // TODO: This code would benefit from having a better way to read into - // uninitialized memory. - let mut buf = [0]; - match crate::backend::net::syscalls::recv( - fd, - &mut buf, - RecvFlags::PEEK | RecvFlags::DONTWAIT, - ) { + let mut buf = [core::mem::MaybeUninit::::uninit()]; + match unsafe { + crate::backend::net::syscalls::recv( + fd, + buf.as_mut_ptr() as *mut u8, + 1, + RecvFlags::PEEK | RecvFlags::DONTWAIT, + ) + } { Ok(0) => read = false, Err(err) => { #[allow(unreachable_patterns)] // `EAGAIN` may equal `EWOULDBLOCK` diff --git a/src/backend/linux_raw/net/syscalls.rs b/src/backend/linux_raw/net/syscalls.rs index f513f1261..726f022f8 100644 --- a/src/backend/linux_raw/net/syscalls.rs +++ b/src/backend/linux_raw/net/syscalls.rs @@ -13,7 +13,7 @@ use super::send_recv::{RecvFlags, SendFlags}; use super::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6}; use crate::backend::c; use crate::backend::conv::{ - by_mut, by_ref, c_int, c_uint, ret, ret_owned_fd, ret_usize, size_of, slice, slice_mut, + by_mut, by_ref, c_int, c_uint, pass_usize, ret, ret_owned_fd, ret_usize, size_of, slice, socklen_t, zero, }; use crate::fd::{BorrowedFd, OwnedFd}; @@ -585,9 +585,12 @@ pub(crate) fn sendto_unix( } #[inline] -pub(crate) fn recv(fd: BorrowedFd<'_>, buf: &mut [u8], flags: RecvFlags) -> io::Result { - let (buf_addr_mut, buf_len) = slice_mut(buf); - +pub(crate) unsafe fn recv( + fd: BorrowedFd<'_>, + buf: *mut u8, + len: usize, + flags: RecvFlags, +) -> io::Result { #[cfg(not(any( target_arch = "aarch64", target_arch = "mips64", @@ -596,8 +599,8 @@ pub(crate) fn recv(fd: BorrowedFd<'_>, buf: &mut [u8], flags: RecvFlags) -> io:: target_arch = "x86", target_arch = "x86_64", )))] - unsafe { - ret_usize(syscall!(__NR_recv, fd, buf_addr_mut, buf_len, flags)) + { + ret_usize(syscall!(__NR_recv, fd, buf, pass_usize(len), flags)) } #[cfg(any( target_arch = "aarch64", @@ -606,26 +609,26 @@ pub(crate) fn recv(fd: BorrowedFd<'_>, buf: &mut [u8], flags: RecvFlags) -> io:: target_arch = "riscv64", target_arch = "x86_64", ))] - unsafe { + { ret_usize(syscall!( __NR_recvfrom, fd, - buf_addr_mut, - buf_len, + buf, + pass_usize(len), flags, zero(), zero() )) } #[cfg(target_arch = "x86")] - unsafe { + { ret_usize(syscall!( __NR_socketcall, x86_sys(SYS_RECV), slice_just_addr::, _>(&[ fd.into(), - buf_addr_mut, - buf_len, + buf.into(), + pass_usize(len), flags.into(), ]) )) @@ -633,51 +636,48 @@ pub(crate) fn recv(fd: BorrowedFd<'_>, buf: &mut [u8], flags: RecvFlags) -> io:: } #[inline] -pub(crate) fn recvfrom( +pub(crate) unsafe fn recvfrom( fd: BorrowedFd<'_>, - buf: &mut [u8], + buf: *mut u8, + len: usize, flags: RecvFlags, ) -> io::Result<(usize, Option)> { - let (buf_addr_mut, buf_len) = slice_mut(buf); - let mut addrlen = core::mem::size_of::() as socklen_t; let mut storage = MaybeUninit::::uninit(); - unsafe { - // `recvfrom` does not write to the storage if the socket is - // connection-oriented sockets, so we initialize the family field to - // `AF_UNSPEC` so that we can detect this case. - initialize_family_to_unspec(storage.as_mut_ptr()); + // `recvfrom` does not write to the storage if the socket is + // connection-oriented sockets, so we initialize the family field to + // `AF_UNSPEC` so that we can detect this case. + initialize_family_to_unspec(storage.as_mut_ptr()); - #[cfg(not(target_arch = "x86"))] - let nread = ret_usize(syscall!( - __NR_recvfrom, - fd, - buf_addr_mut, - buf_len, - flags, - &mut storage, - by_mut(&mut addrlen) - ))?; - #[cfg(target_arch = "x86")] - let nread = ret_usize(syscall!( - __NR_socketcall, - x86_sys(SYS_RECVFROM), - slice_just_addr::, _>(&[ - fd.into(), - buf_addr_mut, - buf_len, - flags.into(), - (&mut storage).into(), - by_mut(&mut addrlen), - ]) - ))?; + #[cfg(not(target_arch = "x86"))] + let nread = ret_usize(syscall!( + __NR_recvfrom, + fd, + buf, + pass_usize(len), + flags, + &mut storage, + by_mut(&mut addrlen) + ))?; + #[cfg(target_arch = "x86")] + let nread = ret_usize(syscall!( + __NR_socketcall, + x86_sys(SYS_RECVFROM), + slice_just_addr::, _>(&[ + fd.into(), + buf.into(), + pass_usize(len), + flags.into(), + (&mut storage).into(), + by_mut(&mut addrlen), + ]) + ))?; - Ok(( - nread, - maybe_read_sockaddr_os(&storage.assume_init(), addrlen.try_into().unwrap()), - )) - } + Ok(( + nread, + maybe_read_sockaddr_os(&storage.assume_init(), addrlen.try_into().unwrap()), + )) } #[inline] diff --git a/src/backend/linux_raw/rand/syscalls.rs b/src/backend/linux_raw/rand/syscalls.rs index e744f3193..c0f497b98 100644 --- a/src/backend/linux_raw/rand/syscalls.rs +++ b/src/backend/linux_raw/rand/syscalls.rs @@ -5,12 +5,15 @@ //! See the `rustix::backend` module documentation for details. #![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] -use crate::backend::conv::{ret_usize, slice_mut}; +use crate::backend::conv::{pass_usize, ret_usize}; use crate::io; use crate::rand::GetRandomFlags; #[inline] -pub(crate) fn getrandom(buf: &mut [u8], flags: GetRandomFlags) -> io::Result { - let (buf_addr_mut, buf_len) = slice_mut(buf); - unsafe { ret_usize(syscall!(__NR_getrandom, buf_addr_mut, buf_len, flags)) } +pub(crate) unsafe fn getrandom( + buf: *mut u8, + cap: usize, + flags: GetRandomFlags, +) -> io::Result { + ret_usize(syscall!(__NR_getrandom, buf, pass_usize(cap), flags)) } diff --git a/src/buffer.rs b/src/buffer.rs new file mode 100644 index 000000000..e4b40c739 --- /dev/null +++ b/src/buffer.rs @@ -0,0 +1,21 @@ +//! Utilities to help with buffering. + +#![allow(unsafe_code)] + +use core::mem::MaybeUninit; +use core::slice; + +/// Split an uninitialized byte slice into initialized and uninitialized parts. +/// +/// # Safety +/// +/// At least `init` bytes must be initialized. +#[inline] +pub(super) unsafe fn split_init( + buf: &mut [MaybeUninit], + init: usize, +) -> (&mut [u8], &mut [MaybeUninit]) { + let (init, uninit) = buf.split_at_mut(init); + let init = slice::from_raw_parts_mut(init.as_mut_ptr() as *mut u8, init.len()); + (init, uninit) +} diff --git a/src/io/read_write.rs b/src/io/read_write.rs index fe454adba..2ed9dd716 100644 --- a/src/io/read_write.rs +++ b/src/io/read_write.rs @@ -1,7 +1,11 @@ //! `read` and `write`, optionally positioned, optionally vectored +#![allow(unsafe_code)] + +use crate::buffer::split_init; use crate::{backend, io}; use backend::fd::AsFd; +use core::mem::MaybeUninit; // Declare `IoSlice` and `IoSliceMut`. #[cfg(not(windows))] @@ -34,7 +38,25 @@ pub use backend::io::types::ReadWriteFlags; /// [glibc]: https://www.gnu.org/software/libc/manual/html_node/I_002fO-Primitives.html#index-reading-from-a-file-descriptor #[inline] pub fn read(fd: Fd, buf: &mut [u8]) -> io::Result { - backend::io::syscalls::read(fd.as_fd(), buf) + 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() as *mut u8, buf.len()) }; + + // Split into the initialized and uninitialized portions. + Ok(unsafe { split_init(buf, length?) }) } /// `write(fd, buf)`—Writes to a stream. @@ -86,7 +108,24 @@ pub fn write(fd: Fd, buf: &[u8]) -> io::Result { /// [illumos]: https://illumos.org/man/2/pread #[inline] pub fn pread(fd: Fd, buf: &mut [u8], offset: u64) -> io::Result { - backend::io::syscalls::pread(fd.as_fd(), buf, offset) + 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( + fd: Fd, + buf: &mut [MaybeUninit], + offset: u64, +) -> io::Result<(&mut [u8], &mut [MaybeUninit])> { + let length = unsafe { + backend::io::syscalls::pread(fd.as_fd(), buf.as_mut_ptr() as *mut u8, buf.len(), offset) + }; + Ok(unsafe { split_init(buf, length?) }) } /// `pwrite(fd, bufs)`—Writes to a file at a given position. diff --git a/src/lib.rs b/src/lib.rs index 170e3f958..52006f733 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -141,6 +141,7 @@ extern crate static_assertions; mod static_assertions; // Internal utilities. +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 a377f65ad..f1ae0ea69 100644 --- a/src/net/send_recv/mod.rs +++ b/src/net/send_recv/mod.rs @@ -1,10 +1,14 @@ //! `recv`, `send`, and variants. +#![allow(unsafe_code)] + +use crate::buffer::split_init; #[cfg(unix)] use crate::net::SocketAddrUnix; use crate::net::{SocketAddr, SocketAddrAny, SocketAddrV4, SocketAddrV6}; use crate::{backend, io}; use backend::fd::{AsFd, BorrowedFd}; +use core::mem::MaybeUninit; pub use backend::net::send_recv::{RecvFlags, SendFlags}; @@ -54,7 +58,25 @@ pub use msg::*; /// [glibc]: https://www.gnu.org/software/libc/manual/html_node/Receiving-Data.html #[inline] pub fn recv(fd: Fd, buf: &mut [u8], flags: RecvFlags) -> io::Result { - backend::net::syscalls::recv(fd.as_fd(), buf, flags) + 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 and the +/// slice that remains uninitialized. +#[inline] +pub fn recv_uninit( + fd: Fd, + buf: &mut [MaybeUninit], + flags: RecvFlags, +) -> io::Result<(&mut [u8], &mut [MaybeUninit])> { + let length = unsafe { + backend::net::syscalls::recv(fd.as_fd(), buf.as_mut_ptr() as *mut u8, buf.len(), flags) + }; + + Ok(unsafe { split_init(buf, length?) }) } /// `send(fd, buf, flags)`—Writes data to a socket. @@ -121,7 +143,27 @@ pub fn recvfrom( buf: &mut [u8], flags: RecvFlags, ) -> io::Result<(usize, Option)> { - backend::net::syscalls::recvfrom(fd.as_fd(), buf, flags) + 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 and the +/// slice that remains uninitialized. +#[allow(clippy::type_complexity)] +#[inline] +pub fn recvfrom_uninit( + fd: Fd, + buf: &mut [MaybeUninit], + flags: RecvFlags, +) -> io::Result<(&mut [u8], &mut [MaybeUninit], Option)> { + let (length, addr) = unsafe { + backend::net::syscalls::recvfrom(fd.as_fd(), buf.as_mut_ptr() as *mut u8, buf.len(), flags)? + }; + let (init, uninit) = unsafe { split_init(buf, length) }; + Ok((init, uninit, 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 3fed0dd08..6f3f5fe2b 100644 --- a/src/rand/getrandom.rs +++ b/src/rand/getrandom.rs @@ -1,4 +1,8 @@ +#![allow(unsafe_code)] + +use crate::buffer::split_init; use crate::{backend, io}; +use core::mem::MaybeUninit; pub use backend::rand::types::GetRandomFlags; @@ -16,5 +20,24 @@ 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 { - backend::rand::syscalls::getrandom(buf, flags) + 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() as *mut u8, buf.len(), flags) + }; + + // Split into the initialized and uninitialized portions. + Ok(unsafe { split_init(buf, length?) }) } diff --git a/src/rand/mod.rs b/src/rand/mod.rs index e767c590d..ec4c11221 100644 --- a/src/rand/mod.rs +++ b/src/rand/mod.rs @@ -4,4 +4,4 @@ mod getrandom; #[cfg(linux_kernel)] -pub use getrandom::{getrandom, GetRandomFlags}; +pub use getrandom::{getrandom, getrandom_uninit, GetRandomFlags}; diff --git a/tests/io/read_write.rs b/tests/io/read_write.rs index fc90e3caa..840660f69 100644 --- a/tests/io/read_write.rs +++ b/tests/io/read_write.rs @@ -68,6 +68,32 @@ fn test_readwrite_p() { assert_eq!(&buf, b"world"); } +#[cfg(feature = "fs")] +#[test] +fn test_readwrite_p_uninit() { + use core::mem::MaybeUninit; + use rustix::fs::{openat, Mode, OFlags, CWD}; + use rustix::io::{pread_uninit, pwrite}; + + let tmp = tempfile::tempdir().unwrap(); + let dir = openat(CWD, tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); + let file = openat( + &dir, + "file", + OFlags::RDWR | OFlags::CREATE | OFlags::TRUNC, + Mode::RUSR | Mode::WUSR, + ) + .unwrap(); + + 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(); + assert_eq!(&init, b"hello"); + let (init, _) = pread_uninit(&file, &mut buf, 300).unwrap(); + assert_eq!(&init, b"world"); +} + #[cfg(not(target_os = "espidf"))] // no readv/writev #[cfg(feature = "fs")] #[test] @@ -121,6 +147,33 @@ fn test_readwrite() { assert_eq!(&buf, b"world"); } +#[cfg(feature = "fs")] +#[test] +fn test_readwrite_uninit() { + use core::mem::MaybeUninit; + use rustix::fs::{openat, seek, Mode, OFlags, SeekFrom, CWD}; + use rustix::io::{read_uninit, write}; + + let tmp = tempfile::tempdir().unwrap(); + let dir = openat(CWD, tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); + let file = openat( + &dir, + "file", + OFlags::RDWR | OFlags::CREATE | OFlags::TRUNC, + Mode::RUSR | Mode::WUSR, + ) + .unwrap(); + + write(&file, b"hello").unwrap(); + 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(); + assert_eq!(&init, b"hello"); + let (init, _) = read_uninit(&file, &mut buf).unwrap(); + assert_eq!(&init, b"world"); +} + #[cfg(all(target_os = "linux", target_env = "gnu"))] #[test] fn test_rwf_values() { diff --git a/tests/rand/getrandom.rs b/tests/rand/getrandom.rs index ac316e447..9ae39f1fb 100644 --- a/tests/rand/getrandom.rs +++ b/tests/rand/getrandom.rs @@ -1,7 +1,16 @@ -use rustix::rand::{getrandom, GetRandomFlags}; +use core::mem::MaybeUninit; +use rustix::rand::{getrandom, getrandom_uninit, GetRandomFlags}; #[test] fn test_getrandom() { let mut buf = [0_u8; 256]; let _ = getrandom(&mut buf, GetRandomFlags::empty()); } + +#[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 combined_len = init.len() + uninit.len(); + assert_eq!(buf.len(), combined_len); +}