7
7
// except according to those terms.
8
8
9
9
//! Implementations that just need to read from a file
10
- extern crate std;
11
-
12
- use crate :: util_libc:: { last_os_error, LazyFd } ;
10
+ use crate :: util_libc:: { last_os_error, open_readonly, sys_fill_exact, LazyFd } ;
13
11
use crate :: Error ;
14
- use core:: mem:: ManuallyDrop ;
15
- use std:: os:: unix:: io:: { FromRawFd , IntoRawFd , RawFd } ;
16
- use std:: { fs:: File , io:: Read } ;
17
12
18
13
#[ cfg( target_os = "redox" ) ]
19
- const FILE_PATH : & str = "rand:" ;
20
- #[ cfg( any( target_os = "android" , target_os = "linux" , target_os = "netbsd" ) ) ]
21
- const FILE_PATH : & str = "/dev/urandom" ;
14
+ const FILE_PATH : & str = "rand:\0 " ;
22
15
#[ cfg( any(
23
16
target_os = "dragonfly" ,
24
17
target_os = "emscripten" ,
@@ -27,32 +20,54 @@ const FILE_PATH: &str = "/dev/urandom";
27
20
target_os = "solaris" ,
28
21
target_os = "illumos"
29
22
) ) ]
30
- const FILE_PATH : & str = "/dev/random" ;
23
+ const FILE_PATH : & str = "/dev/random\0 " ;
31
24
32
25
pub fn getrandom_inner ( dest : & mut [ u8 ] ) -> Result < ( ) , Error > {
33
26
static FD : LazyFd = LazyFd :: new ( ) ;
34
27
let fd = FD . init ( init_file) . ok_or ( last_os_error ( ) ) ?;
35
- let file = ManuallyDrop :: new ( unsafe { File :: from_raw_fd ( fd) } ) ;
36
- let mut file_ref: & File = & file;
28
+ let read = |buf : & mut [ u8 ] | unsafe { libc:: read ( fd, buf. as_mut_ptr ( ) as * mut _ , buf. len ( ) ) } ;
37
29
38
30
if cfg ! ( target_os = "emscripten" ) {
39
31
// `Crypto.getRandomValues` documents `dest` should be at most 65536 bytes.
40
32
for chunk in dest. chunks_mut ( 65536 ) {
41
- file_ref . read_exact ( chunk) ?;
33
+ sys_fill_exact ( chunk, read ) ?;
42
34
}
43
35
} else {
44
- file_ref . read_exact ( dest) ?;
36
+ sys_fill_exact ( dest, read ) ?;
45
37
}
46
38
Ok ( ( ) )
47
39
}
48
40
49
- fn init_file ( ) -> Option < RawFd > {
50
- if FILE_PATH == "/dev/urandom" {
51
- // read one byte from "/dev/random" to ensure that OS RNG has initialized
52
- File :: open ( "/dev/random" )
53
- . ok ( ) ?
54
- . read_exact ( & mut [ 0u8 ; 1 ] )
55
- . ok ( ) ?;
41
+ cfg_if ! {
42
+ if #[ cfg( any( target_os = "android" , target_os = "linux" , target_os = "netbsd" ) ) ] {
43
+ fn init_file( ) -> Option <libc:: c_int> {
44
+ // Poll /dev/random to make sure it is ok to read from /dev/urandom.
45
+ let mut pfd = libc:: pollfd {
46
+ fd: unsafe { open_readonly( "/dev/random\0 " ) ? } ,
47
+ events: libc:: POLLIN ,
48
+ revents: 0 ,
49
+ } ;
50
+
51
+ let ret = loop {
52
+ // A negative timeout means an infinite timeout.
53
+ let res = unsafe { libc:: poll( & mut pfd, 1 , -1 ) } ;
54
+ if res == 1 {
55
+ break unsafe { open_readonly( "/dev/urandom\0 " ) } ;
56
+ } else if res < 0 {
57
+ let e = last_os_error( ) . raw_os_error( ) ;
58
+ if e == Some ( libc:: EINTR ) || e == Some ( libc:: EAGAIN ) {
59
+ continue ;
60
+ }
61
+ }
62
+ // We either hard failed, or poll() returned the wrong pfd.
63
+ break None ;
64
+ } ;
65
+ unsafe { libc:: close( pfd. fd) } ;
66
+ ret
67
+ }
68
+ } else {
69
+ fn init_file( ) -> Option <libc:: c_int> {
70
+ unsafe { open_readonly( FILE_PATH ) }
71
+ }
56
72
}
57
- Some ( File :: open ( FILE_PATH ) . ok ( ) ?. into_raw_fd ( ) )
58
73
}
0 commit comments