summaryrefslogtreecommitdiff
path: root/getrandom/src/util_libc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'getrandom/src/util_libc.rs')
-rw-r--r--getrandom/src/util_libc.rs143
1 files changed, 143 insertions, 0 deletions
diff --git a/getrandom/src/util_libc.rs b/getrandom/src/util_libc.rs
new file mode 100644
index 0000000..5a05170
--- /dev/null
+++ b/getrandom/src/util_libc.rs
@@ -0,0 +1,143 @@
+// Copyright 2019 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+use crate::error::ERRNO_NOT_POSITIVE;
+use crate::util::LazyUsize;
+use crate::Error;
+use core::num::NonZeroU32;
+use core::ptr::NonNull;
+
+cfg_if! {
+ if #[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "android"))] {
+ use libc::__errno as errno_location;
+ } else if #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "redox"))] {
+ use libc::__errno_location as errno_location;
+ } else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] {
+ use libc::___errno as errno_location;
+ } else if #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly"))] {
+ use libc::__error as errno_location;
+ } else if #[cfg(target_os = "haiku")] {
+ use libc::_errnop as errno_location;
+ }
+}
+
+pub fn last_os_error() -> Error {
+ #[cfg(not(target_os = "vxworks"))]
+ let errno = unsafe { *errno_location() };
+ #[cfg(target_os = "vxworks")]
+ let errno = unsafe { libc::errnoGet() };
+ if errno > 0 {
+ Error::from(NonZeroU32::new(errno as u32).unwrap())
+ } else {
+ ERRNO_NOT_POSITIVE
+ }
+}
+
+// Fill a buffer by repeatedly invoking a system call. The `sys_fill` function:
+// - should return -1 and set errno on failure
+// - should return the number of bytes written on success
+pub fn sys_fill_exact(
+ mut buf: &mut [u8],
+ sys_fill: impl Fn(&mut [u8]) -> libc::ssize_t,
+) -> Result<(), Error> {
+ while !buf.is_empty() {
+ let res = sys_fill(buf);
+ if res < 0 {
+ let err = last_os_error();
+ // We should try again if the call was interrupted.
+ if err.raw_os_error() != Some(libc::EINTR) {
+ return Err(err);
+ }
+ } else {
+ // We don't check for EOF (ret = 0) as the data we are reading
+ // should be an infinite stream of random bytes.
+ buf = &mut buf[(res as usize)..];
+ }
+ }
+ Ok(())
+}
+
+// A "weak" binding to a C function that may or may not be present at runtime.
+// Used for supporting newer OS features while still building on older systems.
+// F must be a function pointer of type `unsafe extern "C" fn`. Based off of the
+// weak! macro in libstd.
+pub struct Weak {
+ name: &'static str,
+ addr: LazyUsize,
+}
+
+impl Weak {
+ // Construct a binding to a C function with a given name. This function is
+ // unsafe because `name` _must_ be null terminated.
+ pub const unsafe fn new(name: &'static str) -> Self {
+ Self {
+ name,
+ addr: LazyUsize::new(),
+ }
+ }
+
+ // Return a function pointer if present at runtime. Otherwise, return null.
+ pub fn ptr(&self) -> Option<NonNull<libc::c_void>> {
+ let addr = self.addr.unsync_init(|| unsafe {
+ libc::dlsym(libc::RTLD_DEFAULT, self.name.as_ptr() as *const _) as usize
+ });
+ NonNull::new(addr as *mut _)
+ }
+}
+
+pub struct LazyFd(LazyUsize);
+
+impl LazyFd {
+ pub const fn new() -> Self {
+ Self(LazyUsize::new())
+ }
+
+ // If init() returns Some(x), x should be nonnegative.
+ pub fn init(&self, init: impl FnOnce() -> Option<libc::c_int>) -> Option<libc::c_int> {
+ let fd = self.0.sync_init(
+ || match init() {
+ // OK as val >= 0 and val <= c_int::MAX < usize::MAX
+ Some(val) => val as usize,
+ None => LazyUsize::UNINIT,
+ },
+ || unsafe {
+ // We are usually waiting on an open(2) syscall to complete,
+ // which typically takes < 10us if the file is a device.
+ // However, we might end up waiting much longer if the entropy
+ // pool isn't initialized, but even in that case, this loop will
+ // consume a negligible amount of CPU on most platforms.
+ libc::usleep(10);
+ },
+ );
+ match fd {
+ LazyUsize::UNINIT => None,
+ val => Some(val as libc::c_int),
+ }
+ }
+}
+
+cfg_if! {
+ if #[cfg(any(target_os = "linux", target_os = "emscripten"))] {
+ use libc::open64 as open;
+ } else {
+ use libc::open;
+ }
+}
+
+// SAFETY: path must be null terminated, FD must be manually closed.
+pub unsafe fn open_readonly(path: &str) -> Option<libc::c_int> {
+ debug_assert!(path.as_bytes().last() == Some(&0));
+ let fd = open(path.as_ptr() as *mut _, libc::O_RDONLY | libc::O_CLOEXEC);
+ if fd < 0 {
+ return None;
+ }
+ // O_CLOEXEC works on all Unix targets except for older Linux kernels (pre
+ // 2.6.23), so we also use an ioctl to make sure FD_CLOEXEC is set.
+ #[cfg(target_os = "linux")]
+ libc::ioctl(fd, libc::FIOCLEX);
+ Some(fd)
+}