11use std:: collections:: BTreeMap ;
22use std:: convert:: { TryFrom , TryInto } ;
33use std:: fs:: { read_dir, remove_dir, remove_file, rename, DirBuilder , File , FileType , OpenOptions , ReadDir } ;
4- use std:: io:: { Read , Seek , SeekFrom , Write } ;
4+ use std:: io:: { self , Read , Seek , SeekFrom , Write } ;
55use std:: path:: Path ;
66use std:: time:: SystemTime ;
77
@@ -22,15 +22,42 @@ struct FileHandle {
2222 writable : bool ,
2323}
2424
25+ trait FileDescriptor < ' tcx > : std:: fmt:: Debug {
26+ fn as_file_handle ( & self ) -> InterpResult < ' tcx , & FileHandle > ;
27+
28+ fn read ( & mut self , bytes : & mut [ u8 ] ) -> InterpResult < ' tcx , io:: Result < usize > > ;
29+ fn write ( & mut self , bytes : & [ u8 ] ) -> InterpResult < ' tcx , io:: Result < usize > > ;
30+ fn seek ( & mut self , offset : SeekFrom ) -> InterpResult < ' tcx , io:: Result < u64 > > ;
31+ }
32+
33+ impl < ' tcx > FileDescriptor < ' tcx > for FileHandle {
34+ fn as_file_handle ( & self ) -> InterpResult < ' tcx , & FileHandle > {
35+ Ok ( & self )
36+ }
37+
38+ fn read ( & mut self , bytes : & mut [ u8 ] ) -> InterpResult < ' tcx , io:: Result < usize > > {
39+ Ok ( self . file . read ( bytes) )
40+ }
41+
42+ fn write ( & mut self , bytes : & [ u8 ] ) -> InterpResult < ' tcx , io:: Result < usize > > {
43+ Ok ( self . file . write ( bytes) )
44+ }
45+
46+ fn seek ( & mut self , offset : SeekFrom ) -> InterpResult < ' tcx , io:: Result < u64 > > {
47+ Ok ( self . file . seek ( offset) )
48+ }
49+ }
50+
2551#[ derive( Debug , Default ) ]
26- pub struct FileHandler {
27- handles : BTreeMap < i32 , FileHandle > ,
52+ pub struct FileHandler < ' tcx > {
53+ handles : BTreeMap < i32 , Box < dyn FileDescriptor < ' tcx > > > ,
2854}
2955
56+
3057// fd numbers 0, 1, and 2 are reserved for stdin, stdout, and stderr
3158const MIN_NORMAL_FILE_FD : i32 = 3 ;
3259
33- impl FileHandler {
60+ impl < ' tcx > FileHandler < ' tcx > {
3461 fn insert_fd ( & mut self , file_handle : FileHandle ) -> i32 {
3562 self . insert_fd_with_min_fd ( file_handle, 0 )
3663 }
@@ -62,7 +89,7 @@ impl FileHandler {
6289 self . handles . last_key_value ( ) . map ( |( fd, _) | fd. checked_add ( 1 ) . unwrap ( ) ) . unwrap_or ( min_fd)
6390 } ) ;
6491
65- self . handles . insert ( new_fd, file_handle) . unwrap_none ( ) ;
92+ self . handles . insert ( new_fd, Box :: new ( file_handle) ) . unwrap_none ( ) ;
6693 new_fd
6794 }
6895}
@@ -383,7 +410,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
383410 }
384411 let fh = & mut this. machine . file_handler ;
385412 let ( file_result, writable) = match fh. handles . get ( & fd) {
386- Some ( FileHandle { file, writable } ) => ( file. try_clone ( ) , * writable) ,
413+ Some ( file_descriptor) => match file_descriptor. as_file_handle ( ) {
414+ Ok ( FileHandle { file, writable } ) => ( file. try_clone ( ) , * writable) ,
415+ Err ( _) => return this. handle_not_found ( ) ,
416+ } ,
387417 None => return this. handle_not_found ( ) ,
388418 } ;
389419 let fd_result = file_result. map ( |duplicated| {
@@ -394,9 +424,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
394424 && cmd == this. eval_libc_i32 ( "F_FULLFSYNC" ) ?
395425 {
396426 let & [ _, _] = check_arg_count ( args) ?;
397- if let Some ( FileHandle { file, writable } ) = this. machine . file_handler . handles . get ( & fd) {
398- let io_result = maybe_sync_file ( file, * writable, File :: sync_all) ;
399- this. try_unwrap_io_result ( io_result)
427+ if let Some ( file_descriptor) = this. machine . file_handler . handles . get ( & fd) {
428+ match file_descriptor. as_file_handle ( ) {
429+ Ok ( FileHandle { file, writable } ) => {
430+ let io_result = maybe_sync_file ( & file, * writable, File :: sync_all) ;
431+ this. try_unwrap_io_result ( io_result)
432+ } ,
433+ Err ( _) => this. handle_not_found ( ) ,
434+ }
400435 } else {
401436 this. handle_not_found ( )
402437 }
@@ -412,24 +447,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
412447
413448 let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
414449
415- if let Some ( FileHandle { file, writable } ) = this. machine . file_handler . handles . remove ( & fd) {
416- // We sync the file if it was opened in a mode different than read-only.
417- if writable {
418- // `File::sync_all` does the checks that are done when closing a file. We do this to
419- // to handle possible errors correctly.
420- let result = this. try_unwrap_io_result ( file. sync_all ( ) . map ( |_| 0i32 ) ) ;
421- // Now we actually close the file.
422- drop ( file) ;
423- // And return the result.
424- result
425- } else {
426- // We drop the file, this closes it but ignores any errors produced when closing
427- // it. This is done because `File::sync_all` cannot be done over files like
428- // `/dev/urandom` which are read-only. Check
429- // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439 for a deeper
430- // discussion.
431- drop ( file) ;
432- Ok ( 0 )
450+ if let Some ( file_descriptor) = this. machine . file_handler . handles . remove ( & fd) {
451+ match file_descriptor. as_file_handle ( ) {
452+ Ok ( FileHandle { file, writable } ) => {
453+ // We sync the file if it was opened in a mode different than read-only.
454+ if * writable {
455+ // `File::sync_all` does the checks that are done when closing a file. We do this to
456+ // to handle possible errors correctly.
457+ let result = this. try_unwrap_io_result ( file. sync_all ( ) . map ( |_| 0i32 ) ) ;
458+ // Now we actually close the file.
459+ drop ( file) ;
460+ // And return the result.
461+ result
462+ } else {
463+ // We drop the file, this closes it but ignores any errors produced when closing
464+ // it. This is done because `File::sync_all` cannot be done over files like
465+ // `/dev/urandom` which are read-only. Check
466+ // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439 for a deeper
467+ // discussion.
468+ drop ( file) ;
469+ Ok ( 0 )
470+ }
471+ } ,
472+ Err ( _) => this. handle_not_found ( )
433473 }
434474 } else {
435475 this. handle_not_found ( )
@@ -460,15 +500,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
460500 // host's and target's `isize`. This saves us from having to handle overflows later.
461501 let count = count. min ( this. machine_isize_max ( ) as u64 ) . min ( isize:: MAX as u64 ) ;
462502
463- if let Some ( FileHandle { file , writable : _ } ) = this. machine . file_handler . handles . get_mut ( & fd) {
464- trace ! ( "read: FD mapped to {:?}" , file ) ;
503+ if let Some ( file_descriptor ) = this. machine . file_handler . handles . get_mut ( & fd) {
504+ trace ! ( "read: FD mapped to {:?}" , file_descriptor ) ;
465505 // We want to read at most `count` bytes. We are sure that `count` is not negative
466506 // because it was a target's `usize`. Also we are sure that its smaller than
467507 // `usize::MAX` because it is a host's `isize`.
468508 let mut bytes = vec ! [ 0 ; count as usize ] ;
469- let result = file
470- . read ( & mut bytes)
471- // `File::read` never returns a value larger than `count`, so this cannot fail.
509+ // `File::read` never returns a value larger than `count`,
510+ // so this cannot fail.
511+ let result = file_descriptor
512+ . read ( & mut bytes) ?
472513 . map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
473514
474515 match result {
@@ -510,9 +551,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
510551 // host's and target's `isize`. This saves us from having to handle overflows later.
511552 let count = count. min ( this. machine_isize_max ( ) as u64 ) . min ( isize:: MAX as u64 ) ;
512553
513- if let Some ( FileHandle { file , writable : _ } ) = this. machine . file_handler . handles . get_mut ( & fd) {
554+ if let Some ( file_descriptor ) = this. machine . file_handler . handles . get_mut ( & fd) {
514555 let bytes = this. memory . read_bytes ( buf, Size :: from_bytes ( count) ) ?;
515- let result = file. write ( & bytes) . map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
556+ let result = file_descriptor
557+ . write ( & bytes) ?
558+ . map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
516559 this. try_unwrap_io_result ( result)
517560 } else {
518561 this. handle_not_found ( )
@@ -545,8 +588,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
545588 return Ok ( -1 ) ;
546589 } ;
547590
548- if let Some ( FileHandle { file, writable : _ } ) = this. machine . file_handler . handles . get_mut ( & fd) {
549- let result = file. seek ( seek_from) . map ( |offset| i64:: try_from ( offset) . unwrap ( ) ) ;
591+ if let Some ( file_descriptor) = this. machine . file_handler . handles . get_mut ( & fd) {
592+ let result = file_descriptor
593+ . seek ( seek_from) ?
594+ . map ( |offset| i64:: try_from ( offset) . unwrap ( ) ) ;
550595 this. try_unwrap_io_result ( result)
551596 } else {
552597 this. handle_not_found ( )
@@ -1103,21 +1148,26 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
11031148
11041149 let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
11051150 let length = this. read_scalar ( length_op) ?. to_i64 ( ) ?;
1106- if let Some ( FileHandle { file, writable } ) = this. machine . file_handler . handles . get_mut ( & fd) {
1107- if * writable {
1108- if let Ok ( length) = length. try_into ( ) {
1109- let result = file. set_len ( length) ;
1110- this. try_unwrap_io_result ( result. map ( |_| 0i32 ) )
1111- } else {
1112- let einval = this. eval_libc ( "EINVAL" ) ?;
1113- this. set_last_error ( einval) ?;
1114- Ok ( -1 )
1151+ if let Some ( file_descriptor) = this. machine . file_handler . handles . get_mut ( & fd) {
1152+ match file_descriptor. as_file_handle ( ) {
1153+ Ok ( FileHandle { file, writable } ) => {
1154+ if * writable {
1155+ if let Ok ( length) = length. try_into ( ) {
1156+ let result = file. set_len ( length) ;
1157+ this. try_unwrap_io_result ( result. map ( |_| 0i32 ) )
1158+ } else {
1159+ let einval = this. eval_libc ( "EINVAL" ) ?;
1160+ this. set_last_error ( einval) ?;
1161+ Ok ( -1 )
1162+ }
1163+ } else {
1164+ // The file is not writable
1165+ let einval = this. eval_libc ( "EINVAL" ) ?;
1166+ this. set_last_error ( einval) ?;
1167+ Ok ( -1 )
1168+ }
11151169 }
1116- } else {
1117- // The file is not writable
1118- let einval = this. eval_libc ( "EINVAL" ) ?;
1119- this. set_last_error ( einval) ?;
1120- Ok ( -1 )
1170+ Err ( _) => this. handle_not_found ( )
11211171 }
11221172 } else {
11231173 this. handle_not_found ( )
@@ -1135,9 +1185,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
11351185 this. check_no_isolation ( "fsync" ) ?;
11361186
11371187 let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
1138- if let Some ( FileHandle { file, writable } ) = this. machine . file_handler . handles . get ( & fd) {
1139- let io_result = maybe_sync_file ( file, * writable, File :: sync_all) ;
1140- this. try_unwrap_io_result ( io_result)
1188+ if let Some ( file_descriptor) = this. machine . file_handler . handles . get ( & fd) {
1189+ match file_descriptor. as_file_handle ( ) {
1190+ Ok ( FileHandle { file, writable } ) => {
1191+ let io_result = maybe_sync_file ( & file, * writable, File :: sync_all) ;
1192+ this. try_unwrap_io_result ( io_result)
1193+ }
1194+ Err ( _) => this. handle_not_found ( )
1195+ }
11411196 } else {
11421197 this. handle_not_found ( )
11431198 }
@@ -1149,9 +1204,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
11491204 this. check_no_isolation ( "fdatasync" ) ?;
11501205
11511206 let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
1152- if let Some ( FileHandle { file, writable } ) = this. machine . file_handler . handles . get ( & fd) {
1153- let io_result = maybe_sync_file ( file, * writable, File :: sync_data) ;
1154- this. try_unwrap_io_result ( io_result)
1207+ if let Some ( file_descriptor) = this. machine . file_handler . handles . get ( & fd) {
1208+ match file_descriptor. as_file_handle ( ) {
1209+ Ok ( FileHandle { file, writable } ) => {
1210+ let io_result = maybe_sync_file ( & file, * writable, File :: sync_data) ;
1211+ this. try_unwrap_io_result ( io_result)
1212+ }
1213+ Err ( _) => this. handle_not_found ( )
1214+ }
11551215 } else {
11561216 this. handle_not_found ( )
11571217 }
@@ -1187,9 +1247,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
11871247 return Ok ( -1 ) ;
11881248 }
11891249
1190- if let Some ( FileHandle { file, writable } ) = this. machine . file_handler . handles . get ( & fd) {
1191- let io_result = maybe_sync_file ( file, * writable, File :: sync_data) ;
1192- this. try_unwrap_io_result ( io_result)
1250+ if let Some ( file_descriptor) = this. machine . file_handler . handles . get ( & fd) {
1251+ match file_descriptor. as_file_handle ( ) {
1252+ Ok ( FileHandle { file, writable } ) => {
1253+ let io_result = maybe_sync_file ( & file, * writable, File :: sync_data) ;
1254+ this. try_unwrap_io_result ( io_result)
1255+ } ,
1256+ Err ( _) => this. handle_not_found ( )
1257+ }
11931258 } else {
11941259 this. handle_not_found ( )
11951260 }
@@ -1239,7 +1304,10 @@ impl FileMetadata {
12391304 ) -> InterpResult < ' tcx , Option < FileMetadata > > {
12401305 let option = ecx. machine . file_handler . handles . get ( & fd) ;
12411306 let file = match option {
1242- Some ( FileHandle { file, writable : _ } ) => file,
1307+ Some ( file_descriptor) => match file_descriptor. as_file_handle ( ) {
1308+ Ok ( FileHandle { file, writable : _ } ) => file,
1309+ Err ( _) => return ecx. handle_not_found ( ) . map ( |_: i32 | None ) ,
1310+ } ,
12431311 None => return ecx. handle_not_found ( ) . map ( |_: i32 | None ) ,
12441312 } ;
12451313 let metadata = file. metadata ( ) ;
0 commit comments