1
1
use std:: collections:: BTreeMap ;
2
2
use std:: convert:: { TryFrom , TryInto } ;
3
3
use 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 } ;
5
5
use std:: path:: Path ;
6
6
use std:: time:: SystemTime ;
7
7
@@ -22,15 +22,42 @@ struct FileHandle {
22
22
writable : bool ,
23
23
}
24
24
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
+
25
51
#[ 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 > > > ,
28
54
}
29
55
56
+
30
57
// fd numbers 0, 1, and 2 are reserved for stdin, stdout, and stderr
31
58
const MIN_NORMAL_FILE_FD : i32 = 3 ;
32
59
33
- impl FileHandler {
60
+ impl < ' tcx > FileHandler < ' tcx > {
34
61
fn insert_fd ( & mut self , file_handle : FileHandle ) -> i32 {
35
62
self . insert_fd_with_min_fd ( file_handle, 0 )
36
63
}
@@ -62,7 +89,7 @@ impl FileHandler {
62
89
self . handles . last_key_value ( ) . map ( |( fd, _) | fd. checked_add ( 1 ) . unwrap ( ) ) . unwrap_or ( min_fd)
63
90
} ) ;
64
91
65
- self . handles . insert ( new_fd, file_handle) . unwrap_none ( ) ;
92
+ self . handles . insert ( new_fd, Box :: new ( file_handle) ) . unwrap_none ( ) ;
66
93
new_fd
67
94
}
68
95
}
@@ -383,7 +410,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
383
410
}
384
411
let fh = & mut this. machine . file_handler ;
385
412
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
+ } ,
387
417
None => return this. handle_not_found ( ) ,
388
418
} ;
389
419
let fd_result = file_result. map ( |duplicated| {
@@ -394,9 +424,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
394
424
&& cmd == this. eval_libc_i32 ( "F_FULLFSYNC" ) ?
395
425
{
396
426
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
+ }
400
435
} else {
401
436
this. handle_not_found ( )
402
437
}
@@ -412,24 +447,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
412
447
413
448
let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
414
449
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 ( )
433
473
}
434
474
} else {
435
475
this. handle_not_found ( )
@@ -460,15 +500,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
460
500
// host's and target's `isize`. This saves us from having to handle overflows later.
461
501
let count = count. min ( this. machine_isize_max ( ) as u64 ) . min ( isize:: MAX as u64 ) ;
462
502
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 ) ;
465
505
// We want to read at most `count` bytes. We are sure that `count` is not negative
466
506
// because it was a target's `usize`. Also we are sure that its smaller than
467
507
// `usize::MAX` because it is a host's `isize`.
468
508
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) ?
472
513
. map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
473
514
474
515
match result {
@@ -510,9 +551,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
510
551
// host's and target's `isize`. This saves us from having to handle overflows later.
511
552
let count = count. min ( this. machine_isize_max ( ) as u64 ) . min ( isize:: MAX as u64 ) ;
512
553
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) {
514
555
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 ( ) ) ;
516
559
this. try_unwrap_io_result ( result)
517
560
} else {
518
561
this. handle_not_found ( )
@@ -545,8 +588,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
545
588
return Ok ( -1 ) ;
546
589
} ;
547
590
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 ( ) ) ;
550
595
this. try_unwrap_io_result ( result)
551
596
} else {
552
597
this. handle_not_found ( )
@@ -1103,21 +1148,26 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1103
1148
1104
1149
let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
1105
1150
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
+ }
1115
1169
}
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 ( )
1121
1171
}
1122
1172
} else {
1123
1173
this. handle_not_found ( )
@@ -1135,9 +1185,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1135
1185
this. check_no_isolation ( "fsync" ) ?;
1136
1186
1137
1187
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
+ }
1141
1196
} else {
1142
1197
this. handle_not_found ( )
1143
1198
}
@@ -1149,9 +1204,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1149
1204
this. check_no_isolation ( "fdatasync" ) ?;
1150
1205
1151
1206
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
+ }
1155
1215
} else {
1156
1216
this. handle_not_found ( )
1157
1217
}
@@ -1187,9 +1247,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1187
1247
return Ok ( -1 ) ;
1188
1248
}
1189
1249
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
+ }
1193
1258
} else {
1194
1259
this. handle_not_found ( )
1195
1260
}
@@ -1239,7 +1304,10 @@ impl FileMetadata {
1239
1304
) -> InterpResult < ' tcx , Option < FileMetadata > > {
1240
1305
let option = ecx. machine . file_handler . handles . get ( & fd) ;
1241
1306
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
+ } ,
1243
1311
None => return ecx. handle_not_found ( ) . map ( |_: i32 | None ) ,
1244
1312
} ;
1245
1313
let metadata = file. metadata ( ) ;
0 commit comments