1
1
use std:: any:: Any ;
2
2
use std:: collections:: BTreeMap ;
3
3
use std:: io:: { IsTerminal , Read , SeekFrom , Write } ;
4
+ use std:: marker:: CoercePointee ;
4
5
use std:: ops:: Deref ;
5
6
use std:: rc:: { Rc , Weak } ;
6
7
use std:: { fs, io} ;
@@ -10,16 +11,126 @@ use rustc_abi::Size;
10
11
use crate :: shims:: unix:: UnixFileDescription ;
11
12
use crate :: * ;
12
13
14
+ /// A unique id for file descriptions. While we could use the address, considering that
15
+ /// is definitely unique, the address would expose interpreter internal state when used
16
+ /// for sorting things. So instead we generate a unique id per file description is the name
17
+ /// for all `dup`licates and is never reused.
18
+ #[ derive( Debug , Copy , Clone , Default , Eq , PartialEq , Ord , PartialOrd ) ]
19
+ pub struct FdId ( usize ) ;
20
+
21
+ #[ derive( Debug , Clone ) ]
22
+ struct FdIdWith < T : ?Sized > {
23
+ id : FdId ,
24
+ inner : T ,
25
+ }
26
+
27
+ /// A refcounted pointer to a file description, also tracking the
28
+ /// globally unique ID of this file description.
29
+ #[ repr( transparent) ]
30
+ #[ derive( CoercePointee , Debug ) ]
31
+ pub struct FileDescriptionRef < T : ?Sized > ( Rc < FdIdWith < T > > ) ;
32
+
33
+ impl < T : ?Sized > Clone for FileDescriptionRef < T > {
34
+ fn clone ( & self ) -> Self {
35
+ FileDescriptionRef ( self . 0 . clone ( ) )
36
+ }
37
+ }
38
+
39
+ impl < T : ?Sized > Deref for FileDescriptionRef < T > {
40
+ type Target = T ;
41
+ fn deref ( & self ) -> & T {
42
+ & self . 0 . inner
43
+ }
44
+ }
45
+
46
+ impl < T : ?Sized > FileDescriptionRef < T > {
47
+ pub fn id ( & self ) -> FdId {
48
+ self . 0 . id
49
+ }
50
+ }
51
+
52
+ /// Holds a weak reference to the actual file description.
53
+ #[ derive( Clone , Debug ) ]
54
+ pub struct WeakFileDescriptionRef < T : ?Sized > ( Weak < FdIdWith < T > > ) ;
55
+
56
+ impl < T : ?Sized > FileDescriptionRef < T > {
57
+ pub fn downgrade ( this : & Self ) -> WeakFileDescriptionRef < T > {
58
+ WeakFileDescriptionRef ( Rc :: downgrade ( & this. 0 ) )
59
+ }
60
+ }
61
+
62
+ impl < T : ?Sized > WeakFileDescriptionRef < T > {
63
+ pub fn upgrade ( & self ) -> Option < FileDescriptionRef < T > > {
64
+ self . 0 . upgrade ( ) . map ( FileDescriptionRef )
65
+ }
66
+ }
67
+
68
+ impl < T > VisitProvenance for WeakFileDescriptionRef < T > {
69
+ fn visit_provenance ( & self , _visit : & mut VisitWith < ' _ > ) {
70
+ // A weak reference can never be the only reference to some pointer or place.
71
+ // Since the actual file description is tracked by strong ref somewhere,
72
+ // it is ok to make this a NOP operation.
73
+ }
74
+ }
75
+
76
+ /// A helper trait to indirectly allow downcasting on `Rc<FdIdWith<dyn _>>`.
77
+ /// Ideally we'd just add a `FdIdWith<Self>: Any` bound to the `FileDescription` trait,
78
+ /// but that does not allow upcasting.
79
+ pub trait FileDescriptionExt : ' static {
80
+ fn into_rc_any ( self : FileDescriptionRef < Self > ) -> Rc < dyn Any > ;
81
+
82
+ /// We wrap the regular `close` function generically, so both handle `Rc::into_inner`
83
+ /// and epoll interest management.
84
+ fn close_ref < ' tcx > (
85
+ self : FileDescriptionRef < Self > ,
86
+ communicate_allowed : bool ,
87
+ ecx : & mut MiriInterpCx < ' tcx > ,
88
+ ) -> InterpResult < ' tcx , io:: Result < ( ) > > ;
89
+ }
90
+
91
+ impl < T : FileDescription + ' static > FileDescriptionExt for T {
92
+ fn into_rc_any ( self : FileDescriptionRef < Self > ) -> Rc < dyn Any > {
93
+ self . 0
94
+ }
95
+
96
+ fn close_ref < ' tcx > (
97
+ self : FileDescriptionRef < Self > ,
98
+ communicate_allowed : bool ,
99
+ ecx : & mut MiriInterpCx < ' tcx > ,
100
+ ) -> InterpResult < ' tcx , io:: Result < ( ) > > {
101
+ match Rc :: into_inner ( self . 0 ) {
102
+ Some ( fd) => {
103
+ // Remove entry from the global epoll_event_interest table.
104
+ ecx. machine . epoll_interests . remove ( fd. id ) ;
105
+
106
+ fd. inner . close ( communicate_allowed, ecx)
107
+ }
108
+ None => {
109
+ // Not the last reference.
110
+ interp_ok ( Ok ( ( ) ) )
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ pub type DynFileDescriptionRef = FileDescriptionRef < dyn FileDescription > ;
117
+
118
+ impl FileDescriptionRef < dyn FileDescription > {
119
+ pub fn downcast < T : FileDescription + ' static > ( self ) -> Option < FileDescriptionRef < T > > {
120
+ let inner = self . into_rc_any ( ) . downcast :: < FdIdWith < T > > ( ) . ok ( ) ?;
121
+ Some ( FileDescriptionRef ( inner) )
122
+ }
123
+ }
124
+
13
125
/// Represents an open file description.
14
- pub trait FileDescription : std:: fmt:: Debug + Any {
126
+ pub trait FileDescription : std:: fmt:: Debug + FileDescriptionExt {
15
127
fn name ( & self ) -> & ' static str ;
16
128
17
129
/// Reads as much as possible into the given buffer `ptr`.
18
130
/// `len` indicates how many bytes we should try to read.
19
131
/// `dest` is where the return value should be stored: number of bytes read, or `-1` in case of error.
20
132
fn read < ' tcx > (
21
- & self ,
22
- _self_ref : & FileDescriptionRef ,
133
+ self : FileDescriptionRef < Self > ,
23
134
_communicate_allowed : bool ,
24
135
_ptr : Pointer ,
25
136
_len : usize ,
@@ -33,8 +144,7 @@ pub trait FileDescription: std::fmt::Debug + Any {
33
144
/// `len` indicates how many bytes we should try to write.
34
145
/// `dest` is where the return value should be stored: number of bytes written, or `-1` in case of error.
35
146
fn write < ' tcx > (
36
- & self ,
37
- _self_ref : & FileDescriptionRef ,
147
+ self : FileDescriptionRef < Self > ,
38
148
_communicate_allowed : bool ,
39
149
_ptr : Pointer ,
40
150
_len : usize ,
@@ -54,11 +164,15 @@ pub trait FileDescription: std::fmt::Debug + Any {
54
164
throw_unsup_format ! ( "cannot seek on {}" , self . name( ) ) ;
55
165
}
56
166
167
+ /// Close the file descriptor.
57
168
fn close < ' tcx > (
58
- self : Box < Self > ,
169
+ self ,
59
170
_communicate_allowed : bool ,
60
171
_ecx : & mut MiriInterpCx < ' tcx > ,
61
- ) -> InterpResult < ' tcx , io:: Result < ( ) > > {
172
+ ) -> InterpResult < ' tcx , io:: Result < ( ) > >
173
+ where
174
+ Self : Sized ,
175
+ {
62
176
throw_unsup_format ! ( "cannot close {}" , self . name( ) ) ;
63
177
}
64
178
@@ -77,21 +191,13 @@ pub trait FileDescription: std::fmt::Debug + Any {
77
191
}
78
192
}
79
193
80
- impl dyn FileDescription {
81
- #[ inline( always) ]
82
- pub fn downcast < T : Any > ( & self ) -> Option < & T > {
83
- ( self as & dyn Any ) . downcast_ref ( )
84
- }
85
- }
86
-
87
194
impl FileDescription for io:: Stdin {
88
195
fn name ( & self ) -> & ' static str {
89
196
"stdin"
90
197
}
91
198
92
199
fn read < ' tcx > (
93
- & self ,
94
- _self_ref : & FileDescriptionRef ,
200
+ self : FileDescriptionRef < Self > ,
95
201
communicate_allowed : bool ,
96
202
ptr : Pointer ,
97
203
len : usize ,
@@ -103,7 +209,7 @@ impl FileDescription for io::Stdin {
103
209
// We want isolation mode to be deterministic, so we have to disallow all reads, even stdin.
104
210
helpers:: isolation_abort_error ( "`read` from stdin" ) ?;
105
211
}
106
- let result = Read :: read ( & mut { self } , & mut bytes) ;
212
+ let result = Read :: read ( & mut & * self , & mut bytes) ;
107
213
match result {
108
214
Ok ( read_size) => ecx. return_read_success ( ptr, & bytes, read_size, dest) ,
109
215
Err ( e) => ecx. set_last_error_and_return ( e, dest) ,
@@ -121,8 +227,7 @@ impl FileDescription for io::Stdout {
121
227
}
122
228
123
229
fn write < ' tcx > (
124
- & self ,
125
- _self_ref : & FileDescriptionRef ,
230
+ self : FileDescriptionRef < Self > ,
126
231
_communicate_allowed : bool ,
127
232
ptr : Pointer ,
128
233
len : usize ,
@@ -131,7 +236,7 @@ impl FileDescription for io::Stdout {
131
236
) -> InterpResult < ' tcx > {
132
237
let bytes = ecx. read_bytes_ptr_strip_provenance ( ptr, Size :: from_bytes ( len) ) ?;
133
238
// We allow writing to stderr even with isolation enabled.
134
- let result = Write :: write ( & mut { self } , bytes) ;
239
+ let result = Write :: write ( & mut & * self , bytes) ;
135
240
// Stdout is buffered, flush to make sure it appears on the
136
241
// screen. This is the write() syscall of the interpreted
137
242
// program, we want it to correspond to a write() syscall on
@@ -155,8 +260,7 @@ impl FileDescription for io::Stderr {
155
260
}
156
261
157
262
fn write < ' tcx > (
158
- & self ,
159
- _self_ref : & FileDescriptionRef ,
263
+ self : FileDescriptionRef < Self > ,
160
264
_communicate_allowed : bool ,
161
265
ptr : Pointer ,
162
266
len : usize ,
@@ -166,7 +270,7 @@ impl FileDescription for io::Stderr {
166
270
let bytes = ecx. read_bytes_ptr_strip_provenance ( ptr, Size :: from_bytes ( len) ) ?;
167
271
// We allow writing to stderr even with isolation enabled.
168
272
// No need to flush, stderr is not buffered.
169
- let result = Write :: write ( & mut { self } , bytes) ;
273
+ let result = Write :: write ( & mut & * self , bytes) ;
170
274
match result {
171
275
Ok ( write_size) => ecx. return_write_success ( write_size, dest) ,
172
276
Err ( e) => ecx. set_last_error_and_return ( e, dest) ,
@@ -188,8 +292,7 @@ impl FileDescription for NullOutput {
188
292
}
189
293
190
294
fn write < ' tcx > (
191
- & self ,
192
- _self_ref : & FileDescriptionRef ,
295
+ self : FileDescriptionRef < Self > ,
193
296
_communicate_allowed : bool ,
194
297
_ptr : Pointer ,
195
298
len : usize ,
@@ -201,91 +304,10 @@ impl FileDescription for NullOutput {
201
304
}
202
305
}
203
306
204
- /// Structure contains both the file description and its unique identifier.
205
- #[ derive( Clone , Debug ) ]
206
- pub struct FileDescWithId < T : FileDescription + ?Sized > {
207
- id : FdId ,
208
- file_description : Box < T > ,
209
- }
210
-
211
- #[ derive( Clone , Debug ) ]
212
- pub struct FileDescriptionRef ( Rc < FileDescWithId < dyn FileDescription > > ) ;
213
-
214
- impl Deref for FileDescriptionRef {
215
- type Target = dyn FileDescription ;
216
-
217
- fn deref ( & self ) -> & Self :: Target {
218
- & * self . 0 . file_description
219
- }
220
- }
221
-
222
- impl FileDescriptionRef {
223
- fn new ( fd : impl FileDescription , id : FdId ) -> Self {
224
- FileDescriptionRef ( Rc :: new ( FileDescWithId { id, file_description : Box :: new ( fd) } ) )
225
- }
226
-
227
- pub fn close < ' tcx > (
228
- self ,
229
- communicate_allowed : bool ,
230
- ecx : & mut MiriInterpCx < ' tcx > ,
231
- ) -> InterpResult < ' tcx , io:: Result < ( ) > > {
232
- // Destroy this `Rc` using `into_inner` so we can call `close` instead of
233
- // implicitly running the destructor of the file description.
234
- let id = self . get_id ( ) ;
235
- match Rc :: into_inner ( self . 0 ) {
236
- Some ( fd) => {
237
- // Remove entry from the global epoll_event_interest table.
238
- ecx. machine . epoll_interests . remove ( id) ;
239
-
240
- fd. file_description . close ( communicate_allowed, ecx)
241
- }
242
- None => interp_ok ( Ok ( ( ) ) ) ,
243
- }
244
- }
245
-
246
- pub fn downgrade ( & self ) -> WeakFileDescriptionRef {
247
- WeakFileDescriptionRef { weak_ref : Rc :: downgrade ( & self . 0 ) }
248
- }
249
-
250
- pub fn get_id ( & self ) -> FdId {
251
- self . 0 . id
252
- }
253
- }
254
-
255
- /// Holds a weak reference to the actual file description.
256
- #[ derive( Clone , Debug , Default ) ]
257
- pub struct WeakFileDescriptionRef {
258
- weak_ref : Weak < FileDescWithId < dyn FileDescription > > ,
259
- }
260
-
261
- impl WeakFileDescriptionRef {
262
- pub fn upgrade ( & self ) -> Option < FileDescriptionRef > {
263
- if let Some ( file_desc_with_id) = self . weak_ref . upgrade ( ) {
264
- return Some ( FileDescriptionRef ( file_desc_with_id) ) ;
265
- }
266
- None
267
- }
268
- }
269
-
270
- impl VisitProvenance for WeakFileDescriptionRef {
271
- fn visit_provenance ( & self , _visit : & mut VisitWith < ' _ > ) {
272
- // A weak reference can never be the only reference to some pointer or place.
273
- // Since the actual file description is tracked by strong ref somewhere,
274
- // it is ok to make this a NOP operation.
275
- }
276
- }
277
-
278
- /// A unique id for file descriptions. While we could use the address, considering that
279
- /// is definitely unique, the address would expose interpreter internal state when used
280
- /// for sorting things. So instead we generate a unique id per file description is the name
281
- /// for all `dup`licates and is never reused.
282
- #[ derive( Debug , Copy , Clone , Default , Eq , PartialEq , Ord , PartialOrd ) ]
283
- pub struct FdId ( usize ) ;
284
-
285
307
/// The file descriptor table
286
308
#[ derive( Debug ) ]
287
309
pub struct FdTable {
288
- pub fds : BTreeMap < i32 , FileDescriptionRef > ,
310
+ pub fds : BTreeMap < i32 , DynFileDescriptionRef > ,
289
311
/// Unique identifier for file description, used to differentiate between various file description.
290
312
next_file_description_id : FdId ,
291
313
}
@@ -313,8 +335,9 @@ impl FdTable {
313
335
fds
314
336
}
315
337
316
- pub fn new_ref ( & mut self , fd : impl FileDescription ) -> FileDescriptionRef {
317
- let file_handle = FileDescriptionRef :: new ( fd, self . next_file_description_id ) ;
338
+ pub fn new_ref < T : FileDescription > ( & mut self , fd : T ) -> FileDescriptionRef < T > {
339
+ let file_handle =
340
+ FileDescriptionRef ( Rc :: new ( FdIdWith { id : self . next_file_description_id , inner : fd } ) ) ;
318
341
self . next_file_description_id = FdId ( self . next_file_description_id . 0 . strict_add ( 1 ) ) ;
319
342
file_handle
320
343
}
@@ -325,12 +348,16 @@ impl FdTable {
325
348
self . insert ( fd_ref)
326
349
}
327
350
328
- pub fn insert ( & mut self , fd_ref : FileDescriptionRef ) -> i32 {
351
+ pub fn insert ( & mut self , fd_ref : DynFileDescriptionRef ) -> i32 {
329
352
self . insert_with_min_num ( fd_ref, 0 )
330
353
}
331
354
332
355
/// Insert a file description, giving it a file descriptor that is at least `min_fd_num`.
333
- pub fn insert_with_min_num ( & mut self , file_handle : FileDescriptionRef , min_fd_num : i32 ) -> i32 {
356
+ pub fn insert_with_min_num (
357
+ & mut self ,
358
+ file_handle : DynFileDescriptionRef ,
359
+ min_fd_num : i32 ,
360
+ ) -> i32 {
334
361
// Find the lowest unused FD, starting from min_fd. If the first such unused FD is in
335
362
// between used FDs, the find_map combinator will return it. If the first such unused FD
336
363
// is after all other used FDs, the find_map combinator will return None, and we will use
@@ -356,12 +383,12 @@ impl FdTable {
356
383
new_fd_num
357
384
}
358
385
359
- pub fn get ( & self , fd_num : i32 ) -> Option < FileDescriptionRef > {
386
+ pub fn get ( & self , fd_num : i32 ) -> Option < DynFileDescriptionRef > {
360
387
let fd = self . fds . get ( & fd_num) ?;
361
388
Some ( fd. clone ( ) )
362
389
}
363
390
364
- pub fn remove ( & mut self , fd_num : i32 ) -> Option < FileDescriptionRef > {
391
+ pub fn remove ( & mut self , fd_num : i32 ) -> Option < DynFileDescriptionRef > {
365
392
self . fds . remove ( & fd_num)
366
393
}
367
394
0 commit comments