1
1
use std:: collections:: HashMap ;
2
- use std:: fs:: { File , OpenOptions , remove_file } ;
2
+ use std:: fs:: { remove_file , File , OpenOptions } ;
3
3
use std:: io:: { Read , Write } ;
4
4
5
5
use rustc:: ty:: layout:: Size ;
@@ -125,8 +125,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
125
125
// `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
126
126
// always sets this flag when opening a file. However we still need to check that the
127
127
// file itself is open.
128
- let fd_cloexec = this. eval_libc_i32 ( "FD_CLOEXEC" ) ?;
129
- this. get_handle_and ( fd, |_| Ok ( fd_cloexec) )
128
+ if this. machine . file_handler . handles . contains_key ( & fd) {
129
+ Ok ( this. eval_libc_i32 ( "FD_CLOEXEC" ) ?)
130
+ } else {
131
+ this. handle_not_found ( )
132
+ }
130
133
} else {
131
134
throw_unsup_format ! ( "The {:#x} command is not supported for `fcntl`)" , cmd) ;
132
135
}
@@ -139,9 +142,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
139
142
140
143
let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
141
144
142
- this . remove_handle_and ( fd , | handle, this| {
145
+ if let Some ( handle) = this. machine . file_handler . handles . remove ( & fd ) {
143
146
this. try_unwrap_io_result ( handle. file . sync_all ( ) . map ( |_| 0i32 ) )
144
- } )
147
+ } else {
148
+ this. handle_not_found ( )
149
+ }
145
150
}
146
151
147
152
fn read (
@@ -160,20 +165,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
160
165
return Ok ( 0 ) ;
161
166
}
162
167
let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
163
- let buf_scalar = this. read_scalar ( buf_op) ?. not_undef ( ) ?;
164
-
165
- // Remove the file handle to avoid borrowing issues.
166
- this. remove_handle_and ( fd, |mut handle, this| {
167
- // Don't use `?` to avoid returning before reinserting the handle.
168
- let bytes = this. force_ptr ( buf_scalar) . and_then ( |buf| {
169
- this. memory
170
- . get_mut ( buf. alloc_id ) ?
171
- . get_bytes_mut ( & * this. tcx , buf, Size :: from_bytes ( count) )
172
- . map ( |buffer| handle. file . read ( buffer) )
173
- } ) ;
174
- this. machine . file_handler . handles . insert ( fd, handle) . unwrap_none ( ) ;
175
- this. try_unwrap_io_result ( bytes?. map ( |bytes| bytes as i64 ) )
176
- } )
168
+ let buf = this. read_scalar ( buf_op) ?. not_undef ( ) ?;
169
+
170
+ if let Some ( handle) = this. machine . file_handler . handles . get_mut ( & fd) {
171
+ // We want to read at most `count` bytes
172
+ let mut bytes = vec ! [ 0 ; count as usize ] ;
173
+ let result = handle. file . read ( & mut bytes) . map ( |c| c as i64 ) ;
174
+ let written_count = this. try_unwrap_io_result ( result) ?;
175
+ // `try_unwrap_io_result` returns Ok(`-1`) if `result` is an error. There is no other
176
+ // way of returning `-1` because the `Ok` variant of `result` contains the number of
177
+ // written bytes, which is a possitive value.
178
+ if written_count != -1 {
179
+ // If reading to `bytes` did not fail, we write those bytes to the buffer.
180
+ this. memory . write_bytes ( buf, bytes) ?;
181
+ }
182
+ Ok ( written_count)
183
+ } else {
184
+ this. handle_not_found ( )
185
+ }
177
186
}
178
187
179
188
fn write (
@@ -192,20 +201,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
192
201
return Ok ( 0 ) ;
193
202
}
194
203
let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
195
- let buf = this. force_ptr ( this. read_scalar ( buf_op) ?. not_undef ( ) ?) ?;
196
-
197
- this. remove_handle_and ( fd, |mut handle, this| {
198
- let bytes = this. memory . get ( buf. alloc_id ) . and_then ( |alloc| {
199
- alloc
200
- . get_bytes ( & * this. tcx , buf, Size :: from_bytes ( count) )
201
- . map ( |bytes| handle. file . write ( bytes) . map ( |bytes| bytes as i64 ) )
202
- } ) ;
203
- this. machine . file_handler . handles . insert ( fd, handle) . unwrap_none ( ) ;
204
- this. try_unwrap_io_result ( bytes?)
205
- } )
204
+ let buf = this. read_scalar ( buf_op) ?. not_undef ( ) ?;
205
+
206
+ if let Some ( handle) = this. machine . file_handler . handles . get_mut ( & fd) {
207
+ let bytes = this. memory . read_bytes ( buf, Size :: from_bytes ( count) ) ?;
208
+ let result = handle. file . write ( & bytes) . map ( |c| c as i64 ) ;
209
+ this. try_unwrap_io_result ( result)
210
+ } else {
211
+ this. handle_not_found ( )
212
+ }
206
213
}
207
214
208
- fn unlink ( & mut self , path_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , i32 > {
215
+ fn unlink ( & mut self , path_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , i32 > {
209
216
let this = self . eval_context_mut ( ) ;
210
217
211
218
this. check_no_isolation ( "unlink" ) ?;
@@ -217,49 +224,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
217
224
this. try_unwrap_io_result ( result)
218
225
}
219
226
220
- /// Helper function that gets a `FileHandle` immutable reference and allows to manipulate it
221
- /// using the `f` closure.
222
- ///
223
- /// If the `fd` file descriptor does not correspond to a file, this functions returns `Ok(-1)`
224
- /// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
225
- ///
226
- /// This function uses `T: From<i32>` instead of `i32` directly because some IO related
227
- /// functions return different integer types (like `read`, that returns an `i64`).
228
- fn get_handle_and < F , T : From < i32 > > ( & mut self , fd : i32 , f : F ) -> InterpResult < ' tcx , T >
229
- where
230
- F : Fn ( & FileHandle ) -> InterpResult < ' tcx , T > ,
231
- {
227
+ /// Function used when a handle is not found inside `FileHandler`. It returns `Ok(-1)`and sets
228
+ /// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
229
+ /// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
230
+ /// types (like `read`, that returns an `i64`).
231
+ fn handle_not_found < T : From < i32 > > ( & mut self ) -> InterpResult < ' tcx , T > {
232
232
let this = self . eval_context_mut ( ) ;
233
- if let Some ( handle) = this. machine . file_handler . handles . get ( & fd) {
234
- f ( handle)
235
- } else {
236
- let ebadf = this. eval_libc ( "EBADF" ) ?;
237
- this. set_last_error ( ebadf) ?;
238
- Ok ( ( -1 ) . into ( ) )
239
- }
240
- }
241
-
242
- /// Helper function that removes a `FileHandle` and allows to manipulate it using the `f`
243
- /// closure. This function is quite useful when you need to modify a `FileHandle` but you need
244
- /// to modify `MiriEvalContext` at the same time, so you can modify the handle and reinsert it
245
- /// using `f`.
246
- ///
247
- /// If the `fd` file descriptor does not correspond to a file, this functions returns `Ok(-1)`
248
- /// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
249
- ///
250
- /// This function uses `T: From<i32>` instead of `i32` directly because some IO related
251
- /// functions return different integer types (like `read`, that returns an `i64`).
252
- fn remove_handle_and < F , T : From < i32 > > ( & mut self , fd : i32 , mut f : F ) -> InterpResult < ' tcx , T >
253
- where
254
- F : FnMut ( FileHandle , & mut MiriEvalContext < ' mir , ' tcx > ) -> InterpResult < ' tcx , T > ,
255
- {
256
- let this = self . eval_context_mut ( ) ;
257
- if let Some ( handle) = this. machine . file_handler . handles . remove ( & fd) {
258
- f ( handle, this)
259
- } else {
260
- let ebadf = this. eval_libc ( "EBADF" ) ?;
261
- this. set_last_error ( ebadf) ?;
262
- Ok ( ( -1 ) . into ( ) )
263
- }
233
+ let ebadf = this. eval_libc ( "EBADF" ) ?;
234
+ this. set_last_error ( ebadf) ?;
235
+ Ok ( ( -1 ) . into ( ) )
264
236
}
265
237
}
0 commit comments