77// except according to those terms.
88
99//! 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:: { fill_exact, last_os_error, open_readonly, LazyFd } ;
1311use crate :: Error ;
14- use core:: mem:: ManuallyDrop ;
15- use std:: os:: unix:: io:: { FromRawFd , IntoRawFd , RawFd } ;
16- use std:: { fs:: File , io:: Read } ;
1712
1813#[ cfg( target_os = "redox" ) ]
19- const FILE_PATH : & str = "rand:" ;
14+ const FILE_PATH : & str = "rand:\0 " ;
2015#[ cfg( any( target_os = "android" , target_os = "linux" , target_os = "netbsd" ) ) ]
21- const FILE_PATH : & str = "/dev/urandom" ;
16+ const FILE_PATH : & str = "/dev/urandom\0 " ;
2217#[ cfg( any(
2318 target_os = "dragonfly" ,
2419 target_os = "emscripten" ,
@@ -27,32 +22,50 @@ const FILE_PATH: &str = "/dev/urandom";
2722 target_os = "solaris" ,
2823 target_os = "illumos"
2924) ) ]
30- const FILE_PATH : & str = "/dev/random" ;
25+ const FILE_PATH : & str = "/dev/random\0 " ;
3126
3227pub fn getrandom_inner ( dest : & mut [ u8 ] ) -> Result < ( ) , Error > {
3328 static FD : LazyFd = LazyFd :: new ( ) ;
3429 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;
30+ let read = |buf : & mut [ u8 ] | unsafe { libc:: read ( fd, buf. as_mut_ptr ( ) as * mut _ , buf. len ( ) ) } ;
3731
3832 if cfg ! ( target_os = "emscripten" ) {
3933 // `Crypto.getRandomValues` documents `dest` should be at most 65536 bytes.
4034 for chunk in dest. chunks_mut ( 65536 ) {
41- file_ref . read_exact ( chunk) ?;
35+ fill_exact ( chunk, read ) ?;
4236 }
4337 } else {
44- file_ref . read_exact ( dest) ?;
38+ fill_exact ( dest, read ) ?;
4539 }
4640 Ok ( ( ) )
4741}
4842
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 ( ) ?;
43+ fn init_file ( ) -> Option < libc:: c_int > {
44+ if FILE_PATH == "/dev/urandom\0 " {
45+ // Poll /dev/random to make sure it is ok to read from /dev/urandom.
46+ let mut pfd = libc:: pollfd {
47+ fd : unsafe { open_readonly ( "/dev/random\0 " ) ? } ,
48+ events : libc:: POLLIN ,
49+ revents : 0 ,
50+ } ;
51+
52+ let mut res = -1 ;
53+ while res <= 0 {
54+ // A negative timeout means an infinite timeout.
55+ res = unsafe { libc:: poll ( & mut pfd, 1 , -1 ) } ;
56+ if res < 0 {
57+ match last_os_error ( ) . raw_os_error ( ) {
58+ Some ( libc:: EINTR ) | Some ( libc:: EAGAIN ) => { }
59+ _ => break ,
60+ }
61+ }
62+ }
63+
64+ unsafe { libc:: close ( pfd. fd ) } ;
65+ if res != 1 {
66+ // We either hard failed, or poll() returned the wrong pfd.
67+ return None ;
68+ }
5669 }
57- Some ( File :: open ( FILE_PATH ) . ok ( ) ? . into_raw_fd ( ) )
70+ unsafe { open_readonly ( FILE_PATH ) }
5871}
0 commit comments