@@ -234,6 +234,18 @@ impl Default for DirHandler {
234
234
}
235
235
}
236
236
237
+ fn maybe_sync_file ( file : & File , writable : bool , operation : fn ( & File ) -> std:: io:: Result < ( ) > ) -> std:: io:: Result < i32 > {
238
+ if !writable && cfg ! ( windows) {
239
+ // sync_all() and sync_data() will return an error on Windows hosts if the file is not opened
240
+ // for writing. (FlushFileBuffers requires that the file handle have the
241
+ // GENERIC_WRITE right)
242
+ Ok ( 0i32 )
243
+ } else {
244
+ let result = operation ( file) ;
245
+ result. map ( |_| 0i32 )
246
+ }
247
+ }
248
+
237
249
impl < ' mir , ' tcx : ' mir > EvalContextExt < ' mir , ' tcx > for crate :: MiriEvalContext < ' mir , ' tcx > { }
238
250
pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriEvalContextExt < ' mir , ' tcx > {
239
251
fn open (
@@ -377,6 +389,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
377
389
fh. insert_fd_with_min_fd ( FileHandle { file : duplicated, writable } , start)
378
390
} ) ;
379
391
this. try_unwrap_io_result ( fd_result)
392
+ } else if this. tcx . sess . target . target . target_os == "macos"
393
+ && cmd == this. eval_libc_i32 ( "F_FULLFSYNC" ) ?
394
+ {
395
+ let & [ _, _] = check_arg_count ( args) ?;
396
+ if let Some ( FileHandle { file, writable } ) = this. machine . file_handler . handles . get ( & fd) {
397
+ let io_result = maybe_sync_file ( file, * writable, File :: sync_all) ;
398
+ this. try_unwrap_io_result ( io_result)
399
+ } else {
400
+ this. handle_not_found ( )
401
+ }
380
402
} else {
381
403
throw_unsup_format ! ( "the {:#x} command is not supported for `fcntl`)" , cmd) ;
382
404
}
@@ -1103,6 +1125,77 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1103
1125
this. handle_not_found ( )
1104
1126
}
1105
1127
}
1128
+
1129
+ fn fsync ( & mut self , fd_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , i32 > {
1130
+ // On macOS, `fsync` (unlike `fcntl(F_FULLFSYNC)`) does not wait for the
1131
+ // underlying disk to finish writing. In the interest of host compatibility,
1132
+ // we conservatively implement this with `sync_all`, which
1133
+ // *does* wait for the disk.
1134
+
1135
+ let this = self . eval_context_mut ( ) ;
1136
+
1137
+ this. check_no_isolation ( "fsync" ) ?;
1138
+
1139
+ let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
1140
+ if let Some ( FileHandle { file, writable } ) = this. machine . file_handler . handles . get ( & fd) {
1141
+ let io_result = maybe_sync_file ( file, * writable, File :: sync_all) ;
1142
+ this. try_unwrap_io_result ( io_result)
1143
+ } else {
1144
+ this. handle_not_found ( )
1145
+ }
1146
+ }
1147
+
1148
+ fn fdatasync ( & mut self , fd_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , i32 > {
1149
+ let this = self . eval_context_mut ( ) ;
1150
+
1151
+ this. check_no_isolation ( "fdatasync" ) ?;
1152
+
1153
+ let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
1154
+ if let Some ( FileHandle { file, writable } ) = this. machine . file_handler . handles . get ( & fd) {
1155
+ let io_result = maybe_sync_file ( file, * writable, File :: sync_data) ;
1156
+ this. try_unwrap_io_result ( io_result)
1157
+ } else {
1158
+ this. handle_not_found ( )
1159
+ }
1160
+ }
1161
+
1162
+ fn sync_file_range (
1163
+ & mut self ,
1164
+ fd_op : OpTy < ' tcx , Tag > ,
1165
+ offset_op : OpTy < ' tcx , Tag > ,
1166
+ nbytes_op : OpTy < ' tcx , Tag > ,
1167
+ flags_op : OpTy < ' tcx , Tag > ,
1168
+ ) -> InterpResult < ' tcx , i32 > {
1169
+ let this = self . eval_context_mut ( ) ;
1170
+
1171
+ this. check_no_isolation ( "sync_file_range" ) ?;
1172
+
1173
+ let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
1174
+ let offset = this. read_scalar ( offset_op) ?. to_i64 ( ) ?;
1175
+ let nbytes = this. read_scalar ( nbytes_op) ?. to_i64 ( ) ?;
1176
+ let flags = this. read_scalar ( flags_op) ?. to_i32 ( ) ?;
1177
+
1178
+ if offset < 0 || nbytes < 0 {
1179
+ let einval = this. eval_libc ( "EINVAL" ) ?;
1180
+ this. set_last_error ( einval) ?;
1181
+ return Ok ( -1 ) ;
1182
+ }
1183
+ let allowed_flags = this. eval_libc_i32 ( "SYNC_FILE_RANGE_WAIT_BEFORE" ) ?
1184
+ | this. eval_libc_i32 ( "SYNC_FILE_RANGE_WRITE" ) ?
1185
+ | this. eval_libc_i32 ( "SYNC_FILE_RANGE_WAIT_AFTER" ) ?;
1186
+ if flags & allowed_flags != flags {
1187
+ let einval = this. eval_libc ( "EINVAL" ) ?;
1188
+ this. set_last_error ( einval) ?;
1189
+ return Ok ( -1 ) ;
1190
+ }
1191
+
1192
+ if let Some ( FileHandle { file, writable } ) = this. machine . file_handler . handles . get ( & fd) {
1193
+ let io_result = maybe_sync_file ( file, * writable, File :: sync_data) ;
1194
+ this. try_unwrap_io_result ( io_result)
1195
+ } else {
1196
+ this. handle_not_found ( )
1197
+ }
1198
+ }
1106
1199
}
1107
1200
1108
1201
/// Extracts the number of seconds and nanoseconds elapsed between `time` and the unix epoch when
0 commit comments