Skip to content

Commit

Permalink
use_file: Use std::File instead of DropGuard.
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
briansmith committed Jun 5, 2024
1 parent 7e2590f commit cdb34e3
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 42 deletions.
52 changes: 37 additions & 15 deletions src/use_file.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
//! 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.
/// - On Linux, "/dev/urandom is preferred and sufficient in all use cases".
/// - 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
Expand All @@ -32,18 +36,17 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> 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<libc::c_int, Error> {
const _: () = assert!(core::mem::size_of::<libc::c_int>() == core::mem::size_of::<i32>());
fn get_rng_fd() -> Result<RawFd, Error> {
static FD: AtomicI32 = AtomicI32::new(FD_UNINIT);
fn get_fd() -> Option<libc::c_int> {
fn get_fd() -> Option<RawFd> {
match FD.load(Relaxed) {
FD_UNINIT => None,
val => Some(val),
}
}

#[cold]
fn get_fd_locked() -> Result<libc::c_int, Error> {
fn get_fd_locked() -> Result<RawFd, Error> {
// 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();
Expand All @@ -58,7 +61,9 @@ fn get_rng_fd() -> Result<libc::c_int, Error> {
#[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);
Expand Down Expand Up @@ -105,15 +110,14 @@ fn get_rng_fd() -> Result<libc::c_int, Error> {
// 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.
Expand All @@ -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::<RawOsError>() == core::mem::size_of::<u32>());

match u32::try_from(errno) {
Ok(code) if code != 0 => Error::from_os_error(code),
_ => Error::ERRNO_NOT_POSITIVE,
}
})
}

struct Mutex(UnsafeCell<libc::pthread_mutex_t>);

impl Mutex {
Expand Down
27 changes: 0 additions & 27 deletions src/util_libc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<libc::c_int, Error> {
assert!(path.iter().any(|&b| b == 0));
loop {
let fd = unsafe {
libc::open(
path.as_ptr().cast::<libc::c_char>(),
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<u8>]) -> libc::ssize_t {
Expand Down

0 comments on commit cdb34e3

Please sign in to comment.