11use 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 } ;
44
55use rustc:: ty:: layout:: Size ;
66
@@ -42,16 +42,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4242
4343 let flag = this. read_scalar ( flag_op) ?. to_i32 ( ) ?;
4444
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 ) ;
4768 }
4869
4970 let path_bytes = this
5071 . memory ( )
5172 . read_c_str ( this. read_scalar ( path_op) ?. not_undef ( ) ?) ?;
5273 let path = std:: str:: from_utf8 ( path_bytes)
5374 . 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| {
5577 let mut fh = & mut this. machine . file_handler ;
5678 fh. low += 1 ;
5779 fh. handles . insert ( fh. low , FileHandle { file, flag } ) ;
@@ -70,7 +92,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
7092 let this = self . eval_context_mut ( ) ;
7193
7294 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" )
7496 }
7597
7698 let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
@@ -103,15 +125,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
103125 let this = self . eval_context_mut ( ) ;
104126
105127 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" )
107129 }
108130
109131 let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
110132
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+ } )
115136 }
116137
117138 fn read (
@@ -123,38 +144,71 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
123144 let this = self . eval_context_mut ( ) ;
124145
125146 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" )
127148 }
128149
129150 let tcx = & { this. tcx . tcx } ;
130151
131- let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
132- let buf = this. force_ptr ( this. read_scalar ( buf_op) ?. not_undef ( ) ?) ?;
133152 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 ( ) ?;
134159
135160 // 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+ } )
152206 }
153207
154208 /// Helper function that gets a `FileHandle` immutable reference and allows to manipulate it
155- /// using `f`.
209+ /// using the `f` closure .
156210 ///
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)`
158212 /// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
159213 ///
160214 /// 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
177231 /// to modify `MiriEvalContext` at the same time, so you can modify the handle and reinsert it
178232 /// using `f`.
179233 ///
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)`
181235 /// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
182236 ///
183237 /// 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
201255 ///
202256 /// This function uses `T: From<i32>` instead of `i32` directly because some IO related
203257 /// 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 > {
205262 match result {
206263 Ok ( ok) => Ok ( ok) ,
207264 Err ( e) => {
0 commit comments