22//! standard file descriptors (stdin/stdout/stderr).
33
44use std:: any:: Any ;
5+ use std:: cell:: { Ref , RefCell , RefMut } ;
56use std:: collections:: BTreeMap ;
67use std:: io:: { self , ErrorKind , IsTerminal , Read , SeekFrom , Write } ;
8+ use std:: rc:: Rc ;
79
810use rustc_middle:: ty:: TyCtxt ;
911use rustc_target:: abi:: Size ;
@@ -12,7 +14,7 @@ use crate::shims::unix::*;
1214use crate :: * ;
1315
1416/// Represents an open file descriptor.
15- pub trait FileDescriptor : std:: fmt:: Debug + Any {
17+ pub trait FileDescription : std:: fmt:: Debug + Any {
1618 fn name ( & self ) -> & ' static str ;
1719
1820 fn read < ' tcx > (
@@ -44,21 +46,18 @@ pub trait FileDescriptor: std::fmt::Debug + Any {
4446 fn close < ' tcx > (
4547 self : Box < Self > ,
4648 _communicate_allowed : bool ,
47- ) -> InterpResult < ' tcx , io:: Result < i32 > > {
49+ ) -> InterpResult < ' tcx , io:: Result < ( ) > > {
4850 throw_unsup_format ! ( "cannot close {}" , self . name( ) ) ;
4951 }
5052
51- /// Return a new file descriptor *that refers to the same underlying object*.
52- fn dup ( & mut self ) -> io:: Result < Box < dyn FileDescriptor > > ;
53-
5453 fn is_tty ( & self , _communicate_allowed : bool ) -> bool {
5554 // Most FDs are not tty's and the consequence of a wrong `false` are minor,
5655 // so we use a default impl here.
5756 false
5857 }
5958}
6059
61- impl dyn FileDescriptor {
60+ impl dyn FileDescription {
6261 #[ inline( always) ]
6362 pub fn downcast_ref < T : Any > ( & self ) -> Option < & T > {
6463 ( self as & dyn Any ) . downcast_ref ( )
@@ -70,7 +69,7 @@ impl dyn FileDescriptor {
7069 }
7170}
7271
73- impl FileDescriptor for io:: Stdin {
72+ impl FileDescription for io:: Stdin {
7473 fn name ( & self ) -> & ' static str {
7574 "stdin"
7675 }
@@ -88,16 +87,12 @@ impl FileDescriptor for io::Stdin {
8887 Ok ( Read :: read ( self , bytes) )
8988 }
9089
91- fn dup ( & mut self ) -> io:: Result < Box < dyn FileDescriptor > > {
92- Ok ( Box :: new ( io:: stdin ( ) ) )
93- }
94-
9590 fn is_tty ( & self , communicate_allowed : bool ) -> bool {
9691 communicate_allowed && self . is_terminal ( )
9792 }
9893}
9994
100- impl FileDescriptor for io:: Stdout {
95+ impl FileDescription for io:: Stdout {
10196 fn name ( & self ) -> & ' static str {
10297 "stdout"
10398 }
@@ -120,16 +115,12 @@ impl FileDescriptor for io::Stdout {
120115 Ok ( result)
121116 }
122117
123- fn dup ( & mut self ) -> io:: Result < Box < dyn FileDescriptor > > {
124- Ok ( Box :: new ( io:: stdout ( ) ) )
125- }
126-
127118 fn is_tty ( & self , communicate_allowed : bool ) -> bool {
128119 communicate_allowed && self . is_terminal ( )
129120 }
130121}
131122
132- impl FileDescriptor for io:: Stderr {
123+ impl FileDescription for io:: Stderr {
133124 fn name ( & self ) -> & ' static str {
134125 "stderr"
135126 }
@@ -145,10 +136,6 @@ impl FileDescriptor for io::Stderr {
145136 Ok ( Write :: write ( & mut { self } , bytes) )
146137 }
147138
148- fn dup ( & mut self ) -> io:: Result < Box < dyn FileDescriptor > > {
149- Ok ( Box :: new ( io:: stderr ( ) ) )
150- }
151-
152139 fn is_tty ( & self , communicate_allowed : bool ) -> bool {
153140 communicate_allowed && self . is_terminal ( )
154141 }
@@ -158,7 +145,7 @@ impl FileDescriptor for io::Stderr {
158145#[ derive( Debug ) ]
159146pub struct NullOutput ;
160147
161- impl FileDescriptor for NullOutput {
148+ impl FileDescription for NullOutput {
162149 fn name ( & self ) -> & ' static str {
163150 "stderr and stdout"
164151 }
@@ -172,16 +159,30 @@ impl FileDescriptor for NullOutput {
172159 // We just don't write anything, but report to the user that we did.
173160 Ok ( Ok ( bytes. len ( ) ) )
174161 }
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+ }
175171
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+ }
178179 }
179180}
180181
181182/// The file descriptor table
182183#[ derive( Debug ) ]
183184pub struct FdTable {
184- pub fds : BTreeMap < i32 , Box < dyn FileDescriptor > > ,
185+ pub fds : BTreeMap < i32 , FileDescriptor > ,
185186}
186187
187188impl VisitProvenance for FdTable {
@@ -192,28 +193,24 @@ impl VisitProvenance for FdTable {
192193
193194impl FdTable {
194195 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 ( ) ) ) ;
197198 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 ) ) ;
200201 } 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 ( ) ) ) ;
203204 }
204205 FdTable { fds }
205206 }
206207
207- pub fn insert_fd ( & mut self , file_handle : Box < dyn FileDescriptor > ) -> i32 {
208+ pub fn insert_fd ( & mut self , file_handle : FileDescriptor ) -> i32 {
208209 self . insert_fd_with_min_fd ( file_handle, 0 )
209210 }
210211
211212 /// 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 {
217214 // Find the lowest unused FD, starting from min_fd. If the first such unused FD is in
218215 // between used FDs, the find_map combinator will return it. If the first such unused FD
219216 // is after all other used FDs, the find_map combinator will return None, and we will use
@@ -239,15 +236,22 @@ impl FdTable {
239236 new_fd
240237 }
241238
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 ( ) ) )
244242 }
245243
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 ( ) ) )
248247 }
249248
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 > {
251255 self . fds . remove ( & fd)
252256 }
253257
@@ -296,17 +300,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
296300 }
297301 let start = this. read_scalar ( & args[ 2 ] ) ?. to_i32 ( ) ?;
298302
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) ) ,
310305 None => this. fd_not_found ( ) ,
311306 }
312307 } 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> {
330325
331326 Ok ( Scalar :: from_i32 ( if let Some ( file_descriptor) = this. machine . fds . remove ( fd) {
332327 let result = file_descriptor. close ( this. machine . communicate ( ) ) ?;
328+ // return `0` if close is successful
329+ let result = result. map ( |( ) | 0i32 ) ;
333330 this. try_unwrap_io_result ( result) ?
334331 } else {
335332 this. fd_not_found ( ) ?
@@ -369,32 +366,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
369366 . min ( u64:: try_from ( isize:: MAX ) . unwrap ( ) ) ;
370367 let communicate = this. machine . communicate ( ) ;
371368
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 {
396370 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+ }
398396 }
399397 }
400398
@@ -419,13 +417,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
419417 let communicate = this. machine . communicate ( ) ;
420418
421419 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)
430430 }
431431}
0 commit comments