]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | // Copyright 2019 Developers of the Rand project. |
2 | // | |
3 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
4 | // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
5 | // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your | |
6 | // option. This file may not be copied, modified, or distributed | |
7 | // except according to those terms. | |
8 | #![allow(dead_code)] | |
9 | use crate::{util::LazyUsize, Error}; | |
10 | use core::{num::NonZeroU32, ptr::NonNull}; | |
11 | ||
12 | cfg_if! { | |
13 | if #[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "android"))] { | |
14 | use libc::__errno as errno_location; | |
15 | } else if #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "redox"))] { | |
16 | use libc::__errno_location as errno_location; | |
17 | } else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] { | |
18 | use libc::___errno as errno_location; | |
19 | } else if #[cfg(any(target_os = "macos", target_os = "freebsd"))] { | |
20 | use libc::__error as errno_location; | |
21 | } else if #[cfg(target_os = "haiku")] { | |
22 | use libc::_errnop as errno_location; | |
23 | } | |
24 | } | |
25 | ||
26 | cfg_if! { | |
27 | if #[cfg(target_os = "vxworks")] { | |
28 | use libc::errnoGet as get_errno; | |
29 | } else if #[cfg(target_os = "dragonfly")] { | |
30 | // Until rust-lang/rust#29594 is stable, we cannot get the errno value | |
31 | // on DragonFlyBSD. So we just return an out-of-range errno. | |
32 | unsafe fn get_errno() -> libc::c_int { -1 } | |
33 | } else { | |
34 | unsafe fn get_errno() -> libc::c_int { *errno_location() } | |
35 | } | |
36 | } | |
37 | ||
38 | pub fn last_os_error() -> Error { | |
39 | let errno = unsafe { get_errno() }; | |
40 | if errno > 0 { | |
41 | Error::from(NonZeroU32::new(errno as u32).unwrap()) | |
42 | } else { | |
43 | Error::ERRNO_NOT_POSITIVE | |
44 | } | |
45 | } | |
46 | ||
47 | // Fill a buffer by repeatedly invoking a system call. The `sys_fill` function: | |
48 | // - should return -1 and set errno on failure | |
49 | // - should return the number of bytes written on success | |
50 | pub fn sys_fill_exact( | |
51 | mut buf: &mut [u8], | |
52 | sys_fill: impl Fn(&mut [u8]) -> libc::ssize_t, | |
53 | ) -> Result<(), Error> { | |
54 | while !buf.is_empty() { | |
55 | let res = sys_fill(buf); | |
56 | if res < 0 { | |
57 | let err = last_os_error(); | |
58 | // We should try again if the call was interrupted. | |
59 | if err.raw_os_error() != Some(libc::EINTR) { | |
60 | return Err(err); | |
61 | } | |
62 | } else { | |
63 | // We don't check for EOF (ret = 0) as the data we are reading | |
64 | // should be an infinite stream of random bytes. | |
65 | buf = &mut buf[(res as usize)..]; | |
66 | } | |
67 | } | |
68 | Ok(()) | |
69 | } | |
70 | ||
71 | // A "weak" binding to a C function that may or may not be present at runtime. | |
72 | // Used for supporting newer OS features while still building on older systems. | |
73 | // F must be a function pointer of type `unsafe extern "C" fn`. Based off of the | |
74 | // weak! macro in libstd. | |
75 | pub struct Weak { | |
76 | name: &'static str, | |
77 | addr: LazyUsize, | |
78 | } | |
79 | ||
80 | impl Weak { | |
81 | // Construct a binding to a C function with a given name. This function is | |
82 | // unsafe because `name` _must_ be null terminated. | |
83 | pub const unsafe fn new(name: &'static str) -> Self { | |
84 | Self { | |
85 | name, | |
86 | addr: LazyUsize::new(), | |
87 | } | |
88 | } | |
89 | ||
90 | // Return a function pointer if present at runtime. Otherwise, return null. | |
91 | pub fn ptr(&self) -> Option<NonNull<libc::c_void>> { | |
92 | let addr = self.addr.unsync_init(|| unsafe { | |
93 | libc::dlsym(libc::RTLD_DEFAULT, self.name.as_ptr() as *const _) as usize | |
94 | }); | |
95 | NonNull::new(addr as *mut _) | |
96 | } | |
97 | } | |
98 | ||
99 | cfg_if! { | |
100 | if #[cfg(any(target_os = "linux", target_os = "emscripten"))] { | |
101 | use libc::open64 as open; | |
102 | } else { | |
103 | use libc::open; | |
104 | } | |
105 | } | |
106 | ||
107 | // SAFETY: path must be null terminated, FD must be manually closed. | |
108 | pub unsafe fn open_readonly(path: &str) -> Result<libc::c_int, Error> { | |
109 | debug_assert_eq!(path.as_bytes().last(), Some(&0)); | |
110 | let fd = open(path.as_ptr() as *const _, libc::O_RDONLY | libc::O_CLOEXEC); | |
111 | if fd < 0 { | |
112 | return Err(last_os_error()); | |
113 | } | |
114 | Ok(fd) | |
115 | } |