@@ -28,6 +28,9 @@ trait FileDescriptor : std::fmt::Debug {
28
28
fn read < ' tcx > ( & mut self , communicate_allowed : bool , bytes : & mut [ u8 ] ) -> InterpResult < ' tcx , io:: Result < usize > > ;
29
29
fn write < ' tcx > ( & mut self , communicate_allowed : bool , bytes : & [ u8 ] ) -> InterpResult < ' tcx , io:: Result < usize > > ;
30
30
fn seek < ' tcx > ( & mut self , communicate_allowed : bool , offset : SeekFrom ) -> InterpResult < ' tcx , io:: Result < u64 > > ;
31
+ fn close < ' tcx > ( self : Box < Self > , _communicate_allowed : bool ) -> InterpResult < ' tcx , io:: Result < i32 > > ;
32
+
33
+ fn dup < ' tcx > ( & mut self ) -> io:: Result < Box < dyn FileDescriptor > > ;
31
34
}
32
35
33
36
impl FileDescriptor for FileHandle {
@@ -49,6 +52,34 @@ impl FileDescriptor for FileHandle {
49
52
assert ! ( communicate_allowed, "isolation should have prevented even opening a file" ) ;
50
53
Ok ( self . file . seek ( offset) )
51
54
}
55
+
56
+ fn close < ' tcx > ( self : Box < Self > , communicate_allowed : bool ) -> InterpResult < ' tcx , io:: Result < i32 > > {
57
+ assert ! ( communicate_allowed, "isolation should have prevented even opening a file" ) ;
58
+ // We sync the file if it was opened in a mode different than read-only.
59
+ if self . writable {
60
+ // `File::sync_all` does the checks that are done when closing a file. We do this to
61
+ // to handle possible errors correctly.
62
+ let result = self . file . sync_all ( ) . map ( |_| 0i32 ) ;
63
+ // Now we actually close the file.
64
+ drop ( self ) ;
65
+ // And return the result.
66
+ Ok ( result)
67
+ } else {
68
+ // We drop the file, this closes it but ignores any errors
69
+ // produced when closing it. This is done because
70
+ // `File::sync_all` cannot be done over files like
71
+ // `/dev/urandom` which are read-only. Check
72
+ // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439
73
+ // for a deeper discussion.
74
+ drop ( self ) ;
75
+ Ok ( Ok ( 0 ) )
76
+ }
77
+ }
78
+
79
+ fn dup < ' tcx > ( & mut self ) -> io:: Result < Box < dyn FileDescriptor > > {
80
+ let duplicated = self . file . try_clone ( ) ?;
81
+ Ok ( Box :: new ( FileHandle { file : duplicated, writable : self . writable } ) )
82
+ }
52
83
}
53
84
54
85
impl FileDescriptor for io:: Stdin {
@@ -71,6 +102,14 @@ impl FileDescriptor for io::Stdin {
71
102
fn seek < ' tcx > ( & mut self , _communicate_allowed : bool , _offset : SeekFrom ) -> InterpResult < ' tcx , io:: Result < u64 > > {
72
103
throw_unsup_format ! ( "cannot seek on stdin" ) ;
73
104
}
105
+
106
+ fn close < ' tcx > ( self : Box < Self > , _communicate_allowed : bool ) -> InterpResult < ' tcx , io:: Result < i32 > > {
107
+ throw_unsup_format ! ( "stdin cannot be closed" ) ;
108
+ }
109
+
110
+ fn dup < ' tcx > ( & mut self ) -> io:: Result < Box < dyn FileDescriptor > > {
111
+ Ok ( Box :: new ( io:: stdin ( ) ) )
112
+ }
74
113
}
75
114
76
115
impl FileDescriptor for io:: Stdout {
@@ -98,6 +137,14 @@ impl FileDescriptor for io::Stdout {
98
137
fn seek < ' tcx > ( & mut self , _communicate_allowed : bool , _offset : SeekFrom ) -> InterpResult < ' tcx , io:: Result < u64 > > {
99
138
throw_unsup_format ! ( "cannot seek on stdout" ) ;
100
139
}
140
+
141
+ fn close < ' tcx > ( self : Box < Self > , _communicate_allowed : bool ) -> InterpResult < ' tcx , io:: Result < i32 > > {
142
+ throw_unsup_format ! ( "stdout cannot be closed" ) ;
143
+ }
144
+
145
+ fn dup < ' tcx > ( & mut self ) -> io:: Result < Box < dyn FileDescriptor > > {
146
+ Ok ( Box :: new ( io:: stdout ( ) ) )
147
+ }
101
148
}
102
149
103
150
impl FileDescriptor for io:: Stderr {
@@ -118,6 +165,14 @@ impl FileDescriptor for io::Stderr {
118
165
fn seek < ' tcx > ( & mut self , _communicate_allowed : bool , _offset : SeekFrom ) -> InterpResult < ' tcx , io:: Result < u64 > > {
119
166
throw_unsup_format ! ( "cannot seek on stderr" ) ;
120
167
}
168
+
169
+ fn close < ' tcx > ( self : Box < Self > , _communicate_allowed : bool ) -> InterpResult < ' tcx , io:: Result < i32 > > {
170
+ throw_unsup_format ! ( "stderr cannot be closed" ) ;
171
+ }
172
+
173
+ fn dup < ' tcx > ( & mut self ) -> io:: Result < Box < dyn FileDescriptor > > {
174
+ Ok ( Box :: new ( io:: stderr ( ) ) )
175
+ }
121
176
}
122
177
123
178
#[ derive( Debug ) ]
@@ -137,18 +192,12 @@ impl<'tcx> Default for FileHandler {
137
192
}
138
193
}
139
194
140
-
141
- // fd numbers 0, 1, and 2 are reserved for stdin, stdout, and stderr
142
- const MIN_NORMAL_FILE_FD : i32 = 3 ;
143
-
144
195
impl < ' tcx > FileHandler {
145
- fn insert_fd ( & mut self , file_handle : FileHandle ) -> i32 {
196
+ fn insert_fd ( & mut self , file_handle : Box < dyn FileDescriptor > ) -> i32 {
146
197
self . insert_fd_with_min_fd ( file_handle, 0 )
147
198
}
148
199
149
- fn insert_fd_with_min_fd ( & mut self , file_handle : FileHandle , min_fd : i32 ) -> i32 {
150
- let min_fd = std:: cmp:: max ( min_fd, MIN_NORMAL_FILE_FD ) ;
151
-
200
+ fn insert_fd_with_min_fd ( & mut self , file_handle : Box < dyn FileDescriptor > , min_fd : i32 ) -> i32 {
152
201
// Find the lowest unused FD, starting from min_fd. If the first such unused FD is in
153
202
// between used FDs, the find_map combinator will return it. If the first such unused FD
154
203
// is after all other used FDs, the find_map combinator will return None, and we will use
@@ -173,7 +222,7 @@ impl<'tcx> FileHandler {
173
222
self . handles . last_key_value ( ) . map ( |( fd, _) | fd. checked_add ( 1 ) . unwrap ( ) ) . unwrap_or ( min_fd)
174
223
} ) ;
175
224
176
- self . handles . insert ( new_fd, Box :: new ( file_handle) ) . unwrap_none ( ) ;
225
+ self . handles . insert ( new_fd, file_handle) . unwrap_none ( ) ;
177
226
new_fd
178
227
}
179
228
}
@@ -449,7 +498,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
449
498
450
499
let fd = options. open ( & path) . map ( |file| {
451
500
let fh = & mut this. machine . file_handler ;
452
- fh. insert_fd ( FileHandle { file, writable } )
501
+ fh. insert_fd ( Box :: new ( FileHandle { file, writable } ) )
453
502
} ) ;
454
503
455
504
this. try_unwrap_io_result ( fd)
@@ -489,22 +538,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
489
538
// thus they can share the same implementation here.
490
539
let & [ _, _, start] = check_arg_count ( args) ?;
491
540
let start = this. read_scalar ( start) ?. to_i32 ( ) ?;
492
- if fd < MIN_NORMAL_FILE_FD {
493
- throw_unsup_format ! ( "duplicating file descriptors for stdin, stdout, or stderr is not supported" )
494
- }
541
+
495
542
let fh = & mut this. machine . file_handler ;
496
- let ( file_result, writable) = match fh. handles . get ( & fd) {
543
+
544
+ match fh. handles . get_mut ( & fd) {
497
545
Some ( file_descriptor) => {
498
- // FIXME: Support "dup" for all FDs(stdin, etc)
499
- let FileHandle { file, writable } = file_descriptor. as_file_handle ( ) ?;
500
- ( file. try_clone ( ) , * writable)
546
+ let dup_result = file_descriptor. dup ( ) ;
547
+ match dup_result {
548
+ Ok ( dup_fd) => Ok ( fh. insert_fd_with_min_fd ( dup_fd, start) ) ,
549
+ Err ( e) => {
550
+ this. set_last_error_from_io_error ( e) ?;
551
+ Ok ( -1 )
552
+ }
553
+ }
501
554
} ,
502
555
None => return this. handle_not_found ( ) ,
503
- } ;
504
- let fd_result = file_result. map ( |duplicated| {
505
- fh. insert_fd_with_min_fd ( FileHandle { file : duplicated, writable } , start)
506
- } ) ;
507
- this. try_unwrap_io_result ( fd_result)
556
+ }
508
557
} else if this. tcx . sess . target . target . target_os == "macos"
509
558
&& cmd == this. eval_libc_i32 ( "F_FULLFSYNC" ) ?
510
559
{
@@ -530,26 +579,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
530
579
let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
531
580
532
581
if let Some ( file_descriptor) = this. machine . file_handler . handles . remove ( & fd) {
533
- // FIXME: Support `close` for all FDs(stdin, etc)
534
- let FileHandle { file, writable } = file_descriptor. as_file_handle ( ) ?;
535
- // We sync the file if it was opened in a mode different than read-only.
536
- if * writable {
537
- // `File::sync_all` does the checks that are done when closing a file. We do this to
538
- // to handle possible errors correctly.
539
- let result = this. try_unwrap_io_result ( file. sync_all ( ) . map ( |_| 0i32 ) ) ;
540
- // Now we actually close the file.
541
- drop ( file) ;
542
- // And return the result.
543
- result
544
- } else {
545
- // We drop the file, this closes it but ignores any errors produced when closing
546
- // it. This is done because `File::sync_all` cannot be done over files like
547
- // `/dev/urandom` which are read-only. Check
548
- // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439 for a deeper
549
- // discussion.
550
- drop ( file) ;
551
- Ok ( 0 )
552
- }
582
+ let result = file_descriptor. close ( this. machine . communicate ) ?;
583
+ this. try_unwrap_io_result ( result)
553
584
} else {
554
585
this. handle_not_found ( )
555
586
}
0 commit comments