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