1
1
use std:: collections:: HashMap ;
2
- use std:: fs:: File ;
3
- use std:: io:: Read ;
2
+ use std:: fs:: { File , OpenOptions } ;
3
+ use std:: io:: { Read , Write } ;
4
4
5
5
use rustc:: ty:: layout:: Size ;
6
6
@@ -42,16 +42,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
42
42
43
43
let flag = this. read_scalar ( flag_op) ?. to_i32 ( ) ?;
44
44
45
- if flag != this. eval_libc_i32 ( "O_RDONLY" ) ? && flag != this. eval_libc_i32 ( "O_CLOEXEC" ) ? {
46
- throw_unsup_format ! ( "Unsupported flag {:#x}" , flag) ;
45
+ let mut options = OpenOptions :: new ( ) ;
46
+
47
+ // The first two bits of the flag correspond to the access mode of the file in linux.
48
+ let access_mode = flag & 0b11 ;
49
+
50
+ if access_mode == this. eval_libc_i32 ( "O_RDONLY" ) ? {
51
+ options. read ( true ) ;
52
+ } else if access_mode == this. eval_libc_i32 ( "O_WRONLY" ) ? {
53
+ options. write ( true ) ;
54
+ } else if access_mode == this. eval_libc_i32 ( "O_RDWR" ) ? {
55
+ options. read ( true ) . write ( true ) ;
56
+ } else {
57
+ throw_unsup_format ! ( "Unsupported access mode {:#x}" , access_mode) ;
58
+ }
59
+
60
+ if flag & this. eval_libc_i32 ( "O_APPEND" ) ? != 0 {
61
+ options. append ( true ) ;
62
+ }
63
+ if flag & this. eval_libc_i32 ( "O_TRUNC" ) ? != 0 {
64
+ options. truncate ( true ) ;
65
+ }
66
+ if flag & this. eval_libc_i32 ( "O_CREAT" ) ? != 0 {
67
+ options. create ( true ) ;
47
68
}
48
69
49
70
let path_bytes = this
50
71
. memory ( )
51
72
. read_c_str ( this. read_scalar ( path_op) ?. not_undef ( ) ?) ?;
52
73
let path = std:: str:: from_utf8 ( path_bytes)
53
74
. map_err ( |_| err_unsup_format ! ( "{:?} is not a valid utf-8 string" , path_bytes) ) ?;
54
- let fd = File :: open ( path) . map ( |file| {
75
+
76
+ let fd = options. open ( path) . map ( |file| {
55
77
let mut fh = & mut this. machine . file_handler ;
56
78
fh. low += 1 ;
57
79
fh. handles . insert ( fh. low , FileHandle { file, flag } ) ;
@@ -70,7 +92,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
70
92
let this = self . eval_context_mut ( ) ;
71
93
72
94
if !this. machine . communicate {
73
- throw_unsup_format ! ( "`open ` not available when isolation is enabled" )
95
+ throw_unsup_format ! ( "`fcntl ` not available when isolation is enabled" )
74
96
}
75
97
76
98
let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
@@ -103,15 +125,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
103
125
let this = self . eval_context_mut ( ) ;
104
126
105
127
if !this. machine . communicate {
106
- throw_unsup_format ! ( "`open ` not available when isolation is enabled" )
128
+ throw_unsup_format ! ( "`close ` not available when isolation is enabled" )
107
129
}
108
130
109
131
let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
110
132
111
- this. remove_handle_and (
112
- fd,
113
- |handle, this| this. consume_result ( handle. file . sync_all ( ) . map ( |_| 0i32 ) ) ,
114
- )
133
+ this. remove_handle_and ( fd, |handle, this| {
134
+ this. consume_result ( handle. file . sync_all ( ) . map ( |_| 0i32 ) )
135
+ } )
115
136
}
116
137
117
138
fn read (
@@ -123,38 +144,71 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
123
144
let this = self . eval_context_mut ( ) ;
124
145
125
146
if !this. machine . communicate {
126
- throw_unsup_format ! ( "`open ` not available when isolation is enabled" )
147
+ throw_unsup_format ! ( "`read ` not available when isolation is enabled" )
127
148
}
128
149
129
150
let tcx = & { this. tcx . tcx } ;
130
151
131
- let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
132
- let buf = this. force_ptr ( this. read_scalar ( buf_op) ?. not_undef ( ) ?) ?;
133
152
let count = this. read_scalar ( count_op) ?. to_usize ( & * this. tcx ) ?;
153
+ // Reading zero bytes should not change `buf`
154
+ if count == 0 {
155
+ return Ok ( 0 ) ;
156
+ }
157
+ let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
158
+ let buf_scalar = this. read_scalar ( buf_op) ?. not_undef ( ) ?;
134
159
135
160
// Remove the file handle to avoid borrowing issues
136
- this. remove_handle_and (
137
- fd,
138
- |mut handle, this| {
139
- let bytes = handle
140
- . file
141
- . read ( this. memory_mut ( ) . get_mut ( buf. alloc_id ) ?. get_bytes_mut (
142
- tcx,
143
- buf,
144
- Size :: from_bytes ( count) ,
145
- ) ?)
146
- . map ( |bytes| bytes as i64 ) ;
147
- // Reinsert the file handle
148
- this. machine . file_handler . handles . insert ( fd, handle) ;
149
- this. consume_result ( bytes)
150
- } ,
151
- )
161
+ this. remove_handle_and ( fd, |mut handle, this| {
162
+ // Don't use `?` to avoid returning before reinserting the handle
163
+ let bytes = this. force_ptr ( buf_scalar) . and_then ( |buf| {
164
+ this. memory_mut ( )
165
+ . get_mut ( buf. alloc_id ) ?
166
+ . get_bytes_mut ( tcx, buf, Size :: from_bytes ( count) )
167
+ . map ( |buffer| handle. file . read ( buffer) )
168
+ } ) ;
169
+ // Reinsert the file handle
170
+ this. machine . file_handler . handles . insert ( fd, handle) ;
171
+ this. consume_result ( bytes?. map ( |bytes| bytes as i64 ) )
172
+ } )
173
+ }
174
+
175
+ fn write (
176
+ & mut self ,
177
+ fd_op : OpTy < ' tcx , Tag > ,
178
+ buf_op : OpTy < ' tcx , Tag > ,
179
+ count_op : OpTy < ' tcx , Tag > ,
180
+ ) -> InterpResult < ' tcx , i64 > {
181
+ let this = self . eval_context_mut ( ) ;
182
+
183
+ if !this. machine . communicate {
184
+ throw_unsup_format ! ( "`write` not available when isolation is enabled" )
185
+ }
186
+
187
+ let tcx = & { this. tcx . tcx } ;
188
+
189
+ let count = this. read_scalar ( count_op) ?. to_usize ( & * this. tcx ) ?;
190
+ // Writing zero bytes should not change `buf`
191
+ if count == 0 {
192
+ return Ok ( 0 ) ;
193
+ }
194
+ 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 ( 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) ;
204
+ this. consume_result ( bytes?)
205
+ } )
152
206
}
153
207
154
208
/// Helper function that gets a `FileHandle` immutable reference and allows to manipulate it
155
- /// using `f`.
209
+ /// using the `f` closure .
156
210
///
157
- /// If the `fd` file descriptor does not corresponds to a file, this functions returns `Ok(-1)`
211
+ /// If the `fd` file descriptor does not correspond to a file, this functions returns `Ok(-1)`
158
212
/// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
159
213
///
160
214
/// This function uses `T: From<i32>` instead of `i32` directly because some IO related
@@ -177,7 +231,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
177
231
/// to modify `MiriEvalContext` at the same time, so you can modify the handle and reinsert it
178
232
/// using `f`.
179
233
///
180
- /// If the `fd` file descriptor does not corresponds to a file, this functions returns `Ok(-1)`
234
+ /// If the `fd` file descriptor does not correspond to a file, this functions returns `Ok(-1)`
181
235
/// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
182
236
///
183
237
/// This function uses `T: From<i32>` instead of `i32` directly because some IO related
@@ -201,7 +255,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
201
255
///
202
256
/// This function uses `T: From<i32>` instead of `i32` directly because some IO related
203
257
/// functions return different integer types (like `read`, that returns an `i64`)
204
- fn consume_result < T : From < i32 > > ( & mut self , result : std:: io:: Result < T > ) -> InterpResult < ' tcx , T > {
258
+ fn consume_result < T : From < i32 > > (
259
+ & mut self ,
260
+ result : std:: io:: Result < T > ,
261
+ ) -> InterpResult < ' tcx , T > {
205
262
match result {
206
263
Ok ( ok) => Ok ( ok) ,
207
264
Err ( e) => {
0 commit comments