2
2
//! standard file descriptors (stdin/stdout/stderr).
3
3
4
4
use std:: any:: Any ;
5
+ use std:: cell:: { Ref , RefCell , RefMut } ;
5
6
use std:: collections:: BTreeMap ;
6
7
use std:: io:: { self , ErrorKind , IsTerminal , Read , SeekFrom , Write } ;
8
+ use std:: rc:: Rc ;
7
9
8
10
use rustc_middle:: ty:: TyCtxt ;
9
11
use rustc_target:: abi:: Size ;
@@ -12,7 +14,7 @@ use crate::shims::unix::*;
12
14
use crate :: * ;
13
15
14
16
/// Represents an open file descriptor.
15
- pub trait FileDescriptor : std:: fmt:: Debug + Any {
17
+ pub trait FileDescription : std:: fmt:: Debug + Any {
16
18
fn name ( & self ) -> & ' static str ;
17
19
18
20
fn read < ' tcx > (
@@ -44,21 +46,18 @@ pub trait FileDescriptor: std::fmt::Debug + Any {
44
46
fn close < ' tcx > (
45
47
self : Box < Self > ,
46
48
_communicate_allowed : bool ,
47
- ) -> InterpResult < ' tcx , io:: Result < i32 > > {
49
+ ) -> InterpResult < ' tcx , io:: Result < ( ) > > {
48
50
throw_unsup_format ! ( "cannot close {}" , self . name( ) ) ;
49
51
}
50
52
51
- /// Return a new file descriptor *that refers to the same underlying object*.
52
- fn dup ( & mut self ) -> io:: Result < Box < dyn FileDescriptor > > ;
53
-
54
53
fn is_tty ( & self , _communicate_allowed : bool ) -> bool {
55
54
// Most FDs are not tty's and the consequence of a wrong `false` are minor,
56
55
// so we use a default impl here.
57
56
false
58
57
}
59
58
}
60
59
61
- impl dyn FileDescriptor {
60
+ impl dyn FileDescription {
62
61
#[ inline( always) ]
63
62
pub fn downcast_ref < T : Any > ( & self ) -> Option < & T > {
64
63
( self as & dyn Any ) . downcast_ref ( )
@@ -70,7 +69,7 @@ impl dyn FileDescriptor {
70
69
}
71
70
}
72
71
73
- impl FileDescriptor for io:: Stdin {
72
+ impl FileDescription for io:: Stdin {
74
73
fn name ( & self ) -> & ' static str {
75
74
"stdin"
76
75
}
@@ -88,16 +87,12 @@ impl FileDescriptor for io::Stdin {
88
87
Ok ( Read :: read ( self , bytes) )
89
88
}
90
89
91
- fn dup ( & mut self ) -> io:: Result < Box < dyn FileDescriptor > > {
92
- Ok ( Box :: new ( io:: stdin ( ) ) )
93
- }
94
-
95
90
fn is_tty ( & self , communicate_allowed : bool ) -> bool {
96
91
communicate_allowed && self . is_terminal ( )
97
92
}
98
93
}
99
94
100
- impl FileDescriptor for io:: Stdout {
95
+ impl FileDescription for io:: Stdout {
101
96
fn name ( & self ) -> & ' static str {
102
97
"stdout"
103
98
}
@@ -120,16 +115,12 @@ impl FileDescriptor for io::Stdout {
120
115
Ok ( result)
121
116
}
122
117
123
- fn dup ( & mut self ) -> io:: Result < Box < dyn FileDescriptor > > {
124
- Ok ( Box :: new ( io:: stdout ( ) ) )
125
- }
126
-
127
118
fn is_tty ( & self , communicate_allowed : bool ) -> bool {
128
119
communicate_allowed && self . is_terminal ( )
129
120
}
130
121
}
131
122
132
- impl FileDescriptor for io:: Stderr {
123
+ impl FileDescription for io:: Stderr {
133
124
fn name ( & self ) -> & ' static str {
134
125
"stderr"
135
126
}
@@ -145,10 +136,6 @@ impl FileDescriptor for io::Stderr {
145
136
Ok ( Write :: write ( & mut { self } , bytes) )
146
137
}
147
138
148
- fn dup ( & mut self ) -> io:: Result < Box < dyn FileDescriptor > > {
149
- Ok ( Box :: new ( io:: stderr ( ) ) )
150
- }
151
-
152
139
fn is_tty ( & self , communicate_allowed : bool ) -> bool {
153
140
communicate_allowed && self . is_terminal ( )
154
141
}
@@ -158,7 +145,7 @@ impl FileDescriptor for io::Stderr {
158
145
#[ derive( Debug ) ]
159
146
pub struct NullOutput ;
160
147
161
- impl FileDescriptor for NullOutput {
148
+ impl FileDescription for NullOutput {
162
149
fn name ( & self ) -> & ' static str {
163
150
"stderr and stdout"
164
151
}
@@ -172,16 +159,30 @@ impl FileDescriptor for NullOutput {
172
159
// We just don't write anything, but report to the user that we did.
173
160
Ok ( Ok ( bytes. len ( ) ) )
174
161
}
162
+ }
163
+
164
+ #[ derive( Clone , Debug ) ]
165
+ pub struct FileDescriptor ( Rc < RefCell < Box < dyn FileDescription > > > ) ;
166
+
167
+ impl FileDescriptor {
168
+ pub fn new < T : FileDescription > ( fd : T ) -> Self {
169
+ FileDescriptor ( Rc :: new ( RefCell :: new ( Box :: new ( fd) ) ) )
170
+ }
175
171
176
- fn dup ( & mut self ) -> io:: Result < Box < dyn FileDescriptor > > {
177
- Ok ( Box :: new ( NullOutput ) )
172
+ pub fn close < ' ctx > ( self , communicate_allowed : bool ) -> InterpResult < ' ctx , io:: Result < ( ) > > {
173
+ // Destroy this `Rc` using `into_inner` so we can call `close` instead of
174
+ // implicitly running the destructor of the file description.
175
+ match Rc :: into_inner ( self . 0 ) {
176
+ Some ( fd) => RefCell :: into_inner ( fd) . close ( communicate_allowed) ,
177
+ None => Ok ( Ok ( ( ) ) ) ,
178
+ }
178
179
}
179
180
}
180
181
181
182
/// The file descriptor table
182
183
#[ derive( Debug ) ]
183
184
pub struct FdTable {
184
- pub fds : BTreeMap < i32 , Box < dyn FileDescriptor > > ,
185
+ pub fds : BTreeMap < i32 , FileDescriptor > ,
185
186
}
186
187
187
188
impl VisitProvenance for FdTable {
@@ -192,28 +193,24 @@ impl VisitProvenance for FdTable {
192
193
193
194
impl FdTable {
194
195
pub ( crate ) fn new ( mute_stdout_stderr : bool ) -> FdTable {
195
- let mut fds: BTreeMap < _ , Box < dyn FileDescriptor > > = BTreeMap :: new ( ) ;
196
- fds. insert ( 0i32 , Box :: new ( io:: stdin ( ) ) ) ;
196
+ let mut fds: BTreeMap < _ , FileDescriptor > = BTreeMap :: new ( ) ;
197
+ fds. insert ( 0i32 , FileDescriptor :: new ( io:: stdin ( ) ) ) ;
197
198
if mute_stdout_stderr {
198
- fds. insert ( 1i32 , Box :: new ( NullOutput ) ) ;
199
- fds. insert ( 2i32 , Box :: new ( NullOutput ) ) ;
199
+ fds. insert ( 1i32 , FileDescriptor :: new ( NullOutput ) ) ;
200
+ fds. insert ( 2i32 , FileDescriptor :: new ( NullOutput ) ) ;
200
201
} else {
201
- fds. insert ( 1i32 , Box :: new ( io:: stdout ( ) ) ) ;
202
- fds. insert ( 2i32 , Box :: new ( io:: stderr ( ) ) ) ;
202
+ fds. insert ( 1i32 , FileDescriptor :: new ( io:: stdout ( ) ) ) ;
203
+ fds. insert ( 2i32 , FileDescriptor :: new ( io:: stderr ( ) ) ) ;
203
204
}
204
205
FdTable { fds }
205
206
}
206
207
207
- pub fn insert_fd ( & mut self , file_handle : Box < dyn FileDescriptor > ) -> i32 {
208
+ pub fn insert_fd ( & mut self , file_handle : FileDescriptor ) -> i32 {
208
209
self . insert_fd_with_min_fd ( file_handle, 0 )
209
210
}
210
211
211
212
/// Insert a new FD that is at least `min_fd`.
212
- pub fn insert_fd_with_min_fd (
213
- & mut self ,
214
- file_handle : Box < dyn FileDescriptor > ,
215
- min_fd : i32 ,
216
- ) -> i32 {
213
+ pub fn insert_fd_with_min_fd ( & mut self , file_handle : FileDescriptor , min_fd : i32 ) -> i32 {
217
214
// Find the lowest unused FD, starting from min_fd. If the first such unused FD is in
218
215
// between used FDs, the find_map combinator will return it. If the first such unused FD
219
216
// is after all other used FDs, the find_map combinator will return None, and we will use
@@ -239,15 +236,22 @@ impl FdTable {
239
236
new_fd
240
237
}
241
238
242
- pub fn get ( & self , fd : i32 ) -> Option < & dyn FileDescriptor > {
243
- Some ( & * * self . fds . get ( & fd) ?)
239
+ pub fn get ( & self , fd : i32 ) -> Option < Ref < ' _ , dyn FileDescription > > {
240
+ let fd = self . fds . get ( & fd) ?;
241
+ Some ( Ref :: map ( fd. 0 . borrow ( ) , |fd| fd. as_ref ( ) ) )
244
242
}
245
243
246
- pub fn get_mut ( & mut self , fd : i32 ) -> Option < & mut dyn FileDescriptor > {
247
- Some ( & mut * * self . fds . get_mut ( & fd) ?)
244
+ pub fn get_mut ( & self , fd : i32 ) -> Option < RefMut < ' _ , dyn FileDescription > > {
245
+ let fd = self . fds . get ( & fd) ?;
246
+ Some ( RefMut :: map ( fd. 0 . borrow_mut ( ) , |fd| fd. as_mut ( ) ) )
248
247
}
249
248
250
- pub fn remove ( & mut self , fd : i32 ) -> Option < Box < dyn FileDescriptor > > {
249
+ pub fn dup ( & self , fd : i32 ) -> Option < FileDescriptor > {
250
+ let fd = self . fds . get ( & fd) ?;
251
+ Some ( fd. clone ( ) )
252
+ }
253
+
254
+ pub fn remove ( & mut self , fd : i32 ) -> Option < FileDescriptor > {
251
255
self . fds . remove ( & fd)
252
256
}
253
257
@@ -296,17 +300,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
296
300
}
297
301
let start = this. read_scalar ( & args[ 2 ] ) ?. to_i32 ( ) ?;
298
302
299
- match this. machine . fds . get_mut ( fd) {
300
- Some ( file_descriptor) => {
301
- let dup_result = file_descriptor. dup ( ) ;
302
- match dup_result {
303
- Ok ( dup_fd) => Ok ( this. machine . fds . insert_fd_with_min_fd ( dup_fd, start) ) ,
304
- Err ( e) => {
305
- this. set_last_error_from_io_error ( e. kind ( ) ) ?;
306
- Ok ( -1 )
307
- }
308
- }
309
- }
303
+ match this. machine . fds . dup ( fd) {
304
+ Some ( dup_fd) => Ok ( this. machine . fds . insert_fd_with_min_fd ( dup_fd, start) ) ,
310
305
None => this. fd_not_found ( ) ,
311
306
}
312
307
} else if this. tcx . sess . target . os == "macos" && cmd == this. eval_libc_i32 ( "F_FULLFSYNC" ) {
@@ -330,6 +325,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
330
325
331
326
Ok ( Scalar :: from_i32 ( if let Some ( file_descriptor) = this. machine . fds . remove ( fd) {
332
327
let result = file_descriptor. close ( this. machine . communicate ( ) ) ?;
328
+ // return `0` if close is successful
329
+ let result = result. map ( |( ) | 0i32 ) ;
333
330
this. try_unwrap_io_result ( result) ?
334
331
} else {
335
332
this. fd_not_found ( ) ?
@@ -369,32 +366,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
369
366
. min ( u64:: try_from ( isize:: MAX ) . unwrap ( ) ) ;
370
367
let communicate = this. machine . communicate ( ) ;
371
368
372
- if let Some ( file_descriptor) = this. machine . fds . get_mut ( fd) {
373
- trace ! ( "read: FD mapped to {:?}" , file_descriptor) ;
374
- // We want to read at most `count` bytes. We are sure that `count` is not negative
375
- // because it was a target's `usize`. Also we are sure that its smaller than
376
- // `usize::MAX` because it is bounded by the host's `isize`.
377
- let mut bytes = vec ! [ 0 ; usize :: try_from( count) . unwrap( ) ] ;
378
- // `File::read` never returns a value larger than `count`,
379
- // so this cannot fail.
380
- let result = file_descriptor
381
- . read ( communicate, & mut bytes, * this. tcx ) ?
382
- . map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
383
-
384
- match result {
385
- Ok ( read_bytes) => {
386
- // If reading to `bytes` did not fail, we write those bytes to the buffer.
387
- this. write_bytes_ptr ( buf, bytes) ?;
388
- Ok ( read_bytes)
389
- }
390
- Err ( e) => {
391
- this. set_last_error_from_io_error ( e. kind ( ) ) ?;
392
- Ok ( -1 )
393
- }
394
- }
395
- } else {
369
+ let Some ( mut file_descriptor) = this. machine . fds . get_mut ( fd) else {
396
370
trace ! ( "read: FD not found" ) ;
397
- this. fd_not_found ( )
371
+ return this. fd_not_found ( ) ;
372
+ } ;
373
+
374
+ trace ! ( "read: FD mapped to {:?}" , file_descriptor) ;
375
+ // We want to read at most `count` bytes. We are sure that `count` is not negative
376
+ // because it was a target's `usize`. Also we are sure that its smaller than
377
+ // `usize::MAX` because it is bounded by the host's `isize`.
378
+ let mut bytes = vec ! [ 0 ; usize :: try_from( count) . unwrap( ) ] ;
379
+ // `File::read` never returns a value larger than `count`,
380
+ // so this cannot fail.
381
+ let result = file_descriptor
382
+ . read ( communicate, & mut bytes, * this. tcx ) ?
383
+ . map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
384
+ drop ( file_descriptor) ;
385
+
386
+ match result {
387
+ Ok ( read_bytes) => {
388
+ // If reading to `bytes` did not fail, we write those bytes to the buffer.
389
+ this. write_bytes_ptr ( buf, bytes) ?;
390
+ Ok ( read_bytes)
391
+ }
392
+ Err ( e) => {
393
+ this. set_last_error_from_io_error ( e. kind ( ) ) ?;
394
+ Ok ( -1 )
395
+ }
398
396
}
399
397
}
400
398
@@ -419,13 +417,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
419
417
let communicate = this. machine . communicate ( ) ;
420
418
421
419
let bytes = this. read_bytes_ptr_strip_provenance ( buf, Size :: from_bytes ( count) ) ?. to_owned ( ) ;
422
- if let Some ( file_descriptor) = this. machine . fds . get_mut ( fd) {
423
- let result = file_descriptor
424
- . write ( communicate, & bytes, * this. tcx ) ?
425
- . map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
426
- this. try_unwrap_io_result ( result)
427
- } else {
428
- this. fd_not_found ( )
429
- }
420
+ let Some ( mut file_descriptor) = this. machine . fds . get_mut ( fd) else {
421
+ return this. fd_not_found ( ) ;
422
+ } ;
423
+
424
+ let result = file_descriptor
425
+ . write ( communicate, & bytes, * this. tcx ) ?
426
+ . map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
427
+ drop ( file_descriptor) ;
428
+
429
+ this. try_unwrap_io_result ( result)
430
430
}
431
431
}
0 commit comments