From cdb34e33d9959f86afa7ea2c5169c213797e706b Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 5 Jun 2024 15:15:55 -0700 Subject: [PATCH] use_file: Use `std::File` instead of `DropGuard`. For now, still use `libc::{poll,read}`. But use `File::open` to open the files, instead of using `DropGuard`. While doing this, switch to the `RawFd` type alias from `libc::c_int`. --- src/use_file.rs | 52 ++++++++++++++++++++++++++++++++++-------------- src/util_libc.rs | 27 ------------------------- 2 files changed, 37 insertions(+), 42 deletions(-) diff --git a/src/use_file.rs b/src/use_file.rs index cfffed33..695ff8cc 100644 --- a/src/use_file.rs +++ b/src/use_file.rs @@ -1,14 +1,18 @@ //! Implementations that just need to read from a file -use crate::{ - util_libc::{open_readonly, sys_fill_exact}, - Error, -}; + +extern crate std; + +use crate::{util_libc::sys_fill_exact, Error}; use core::{ cell::UnsafeCell, ffi::c_void, mem::MaybeUninit, sync::atomic::{AtomicI32, Ordering::Relaxed}, }; +use std::{ + fs, io, + os::unix::io::{IntoRawFd as _, RawFd}, +}; /// For all platforms, we use `/dev/urandom` rather than `/dev/random`. /// For more information see the linked man pages in lib.rs. @@ -16,7 +20,7 @@ use core::{ /// - On Redox, only /dev/urandom is provided. /// - On AIX, /dev/urandom will "provide cryptographically secure output". /// - On Haiku and QNX Neutrino they are identical. -const FILE_PATH: &[u8] = b"/dev/urandom\0"; +const FILE_PATH: &str = "/dev/urandom"; const FD_UNINIT: i32 = i32::MIN; // Do not inline this when it is the fallback implementation, but don't mark it @@ -32,10 +36,9 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { // Returns the file descriptor for the device file used to retrieve random // bytes. The file will be opened exactly once. All subsequent calls will // return the same file descriptor. This file descriptor is never closed. -fn get_rng_fd() -> Result { - const _: () = assert!(core::mem::size_of::() == core::mem::size_of::()); +fn get_rng_fd() -> Result { static FD: AtomicI32 = AtomicI32::new(FD_UNINIT); - fn get_fd() -> Option { + fn get_fd() -> Option { match FD.load(Relaxed) { FD_UNINIT => None, val => Some(val), @@ -43,7 +46,7 @@ fn get_rng_fd() -> Result { } #[cold] - fn get_fd_locked() -> Result { + fn get_fd_locked() -> Result { // SAFETY: We use the mutex only in this method, and we always unlock it // before returning, making sure we don't violate the pthread_mutex_t API. static MUTEX: Mutex = Mutex::new(); @@ -58,7 +61,9 @@ fn get_rng_fd() -> Result { #[cfg(any(target_os = "android", target_os = "linux"))] wait_until_rng_ready()?; - let fd = open_readonly(FILE_PATH)?; + let file = fs::File::open(FILE_PATH).map_err(map_io_error)?; + + let fd = file.into_raw_fd(); // The fd always fits in a usize without conflicting with FD_UNINIT. debug_assert!(fd >= 0); const _: () = assert!(FD_UNINIT < 0); @@ -105,15 +110,14 @@ fn get_rng_fd() -> Result { // libsodium uses `libc::poll` similarly to this. #[cfg(any(target_os = "android", target_os = "linux"))] fn wait_until_rng_ready() -> Result<(), Error> { - let fd = open_readonly(b"/dev/random\0")?; + use std::os::unix::io::AsRawFd as _; + + let file = fs::File::open("/dev/random").map_err(map_io_error)?; let mut pfd = libc::pollfd { - fd, + fd: file.as_raw_fd(), events: libc::POLLIN, revents: 0, }; - let _guard = DropGuard(|| unsafe { - libc::close(fd); - }); loop { // A negative timeout means an infinite timeout. @@ -130,6 +134,24 @@ fn wait_until_rng_ready() -> Result<(), Error> { } } +fn map_io_error(err: io::Error) -> Error { + // TODO(MSRV feature(raw_os_error_ty)): Use `std::io::RawOsError`. + type RawOsError = i32; + + err.raw_os_error() + .map_or(Error::UNEXPECTED, |errno: RawOsError| { + // RawOsError-to-u32 conversion is lossless for nonnegative values + // if they are the same size. + const _: () = + assert!(core::mem::size_of::() == core::mem::size_of::()); + + match u32::try_from(errno) { + Ok(code) if code != 0 => Error::from_os_error(code), + _ => Error::ERRNO_NOT_POSITIVE, + } + }) +} + struct Mutex(UnsafeCell); impl Mutex { diff --git a/src/util_libc.rs b/src/util_libc.rs index 5095cf90..95c3c1ce 100644 --- a/src/util_libc.rs +++ b/src/util_libc.rs @@ -73,33 +73,6 @@ pub fn sys_fill_exact( Ok(()) } -/// Open a file in read-only mode. -/// -/// # Panics -/// If `path` does not contain any zeros. -// TODO: Move `path` to `CStr` and use `CStr::from_bytes_until_nul` (MSRV 1.69) -// or C-string literals (MSRV 1.77) for statics -#[inline(always)] -pub fn open_readonly(path: &[u8]) -> Result { - assert!(path.iter().any(|&b| b == 0)); - loop { - let fd = unsafe { - libc::open( - path.as_ptr().cast::(), - libc::O_RDONLY | libc::O_CLOEXEC, - ) - }; - if fd >= 0 { - return Ok(fd); - } - let err = last_os_error(); - // We should try again if open() was interrupted. - if err.raw_os_error() != Some(libc::EINTR) { - return Err(err); - } - } -} - /// Thin wrapper around the `getrandom()` Linux system call #[cfg(any(target_os = "android", target_os = "linux"))] pub fn getrandom_syscall(buf: &mut [MaybeUninit]) -> libc::ssize_t {