diff --git a/src/shims/foreign_items/posix.rs b/src/shims/foreign_items/posix.rs index 951a40293b7..6311f0a4a9f 100644 --- a/src/shims/foreign_items/posix.rs +++ b/src/shims/foreign_items/posix.rs @@ -136,6 +136,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // "lseek" is only used on macOS which is 64bit-only, so `i64` always works. this.write_scalar(Scalar::from_i64(result), dest)?; } + "fsync" => { + let result = this.fsync(args[0])?; + this.write_scalar(Scalar::from_i32(result), dest)?; + } + "fdatasync" => { + let result = this.fdatasync(args[0])?; + this.write_scalar(Scalar::from_i32(result), dest)?; + } // Allocation "posix_memalign" => { diff --git a/src/shims/foreign_items/posix/linux.rs b/src/shims/foreign_items/posix/linux.rs index e0f54cac157..16d7d059e73 100644 --- a/src/shims/foreign_items/posix/linux.rs +++ b/src/shims/foreign_items/posix/linux.rs @@ -54,6 +54,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // fadvise is only informational, we can ignore it. this.write_null(dest)?; } + // Linux-only + "sync_file_range" => { + let result = this.sync_file_range(args[0], args[1], args[2], args[3])?; + this.write_scalar(Scalar::from_i32(result), dest)?; + } // Time related shims "clock_gettime" => { diff --git a/src/shims/fs.rs b/src/shims/fs.rs index 8613f6bb099..b7579f6cb73 100644 --- a/src/shims/fs.rs +++ b/src/shims/fs.rs @@ -375,6 +375,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fh.insert_fd_with_min_fd(FileHandle { file: duplicated, writable }, start) }); this.try_unwrap_io_result(fd_result) + } else if this.tcx.sess.target.target.target_os == "macos" + && cmd == this.eval_libc_i32("F_FULLFSYNC")? + { + if let Some(FileHandle { file, writable: _ }) = this.machine.file_handler.handles.get_mut(&fd) { + let result = file.sync_all(); + this.try_unwrap_io_result(result.map(|_| 0i32)) + } else { + this.handle_not_found() + } } else { throw_unsup_format!("the {:#x} command is not supported for `fcntl`)", cmd); } @@ -1103,6 +1112,59 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.handle_not_found() } } + + fn fsync(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + this.check_no_isolation("fsync")?; + + let fd = this.read_scalar(fd_op)?.to_i32()?; + if let Some(FileHandle { file, writable: _ }) = this.machine.file_handler.handles.get_mut(&fd) { + let result = file.sync_all(); + this.try_unwrap_io_result(result.map(|_| 0i32)) + } else { + this.handle_not_found() + } + } + + fn fdatasync(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + this.check_no_isolation("fdatasync")?; + + let fd = this.read_scalar(fd_op)?.to_i32()?; + if let Some(FileHandle { file, writable: _ }) = this.machine.file_handler.handles.get_mut(&fd) { + let result = file.sync_data(); + this.try_unwrap_io_result(result.map(|_| 0i32)) + } else { + this.handle_not_found() + } + } + + fn sync_file_range( + &mut self, + fd_op: OpTy<'tcx, Tag>, + offset_op: OpTy<'tcx, Tag>, + nbytes_op: OpTy<'tcx, Tag>, + flags_op: OpTy<'tcx, Tag>, + ) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + this.check_no_isolation("sync_file_range")?; + + let fd = this.read_scalar(fd_op)?.to_i32()?; + let _offset = this.read_scalar(offset_op)?.to_i64()?; + let _nbytes = this.read_scalar(nbytes_op)?.to_i64()?; + let _flags = this.read_scalar(flags_op)?.to_u32()?; + if let Some(FileHandle { file, writable: _ }) = this.machine.file_handler.handles.get_mut(&fd) { + // In the interest of host compatibility, we conservatively ignore + // offset, nbytes, and flags, and sync the entire file. + let result = file.sync_data(); + this.try_unwrap_io_result(result.map(|_| 0i32)) + } else { + this.handle_not_found() + } + } } /// Extracts the number of seconds and nanoseconds elapsed between `time` and the unix epoch when diff --git a/tests/run-pass/fs.rs b/tests/run-pass/fs.rs index 1a139de8148..df022a7c70d 100644 --- a/tests/run-pass/fs.rs +++ b/tests/run-pass/fs.rs @@ -14,6 +14,8 @@ fn main() { test_seek(); test_metadata(); test_file_set_len(); + test_file_sync_all(); + test_file_sync_data(); test_symlink(); test_errors(); test_rename(); @@ -182,6 +184,28 @@ fn test_file_set_len() { remove_file(&path).unwrap(); } +fn test_file_sync_all() { + let bytes = b"Hello, World!\n"; + let path = prepare_with_content("miri_test_fs_sync_all.txt", bytes); + + // Test that we can call sync_all (can't readily test effects of this operation) + let file = File::open(&path).unwrap(); + file.sync_all().unwrap(); + + remove_file(&path).unwrap(); +} + +fn test_file_sync_data() { + let bytes = b"Hello, World!\n"; + let path = prepare_with_content("miri_test_fs_sync_data.txt", bytes); + + // Test that we can call sync_data (can't readily test effects of this operation) + let file = File::open(&path).unwrap(); + file.sync_data().unwrap(); + + remove_file(&path).unwrap(); +} + fn test_symlink() { let bytes = b"Hello, World!\n"; let path = prepare_with_content("miri_test_fs_link_target.txt", bytes); diff --git a/tests/run-pass/libc.rs b/tests/run-pass/libc.rs index 04ca5c0b3b1..5897c46f637 100644 --- a/tests/run-pass/libc.rs +++ b/tests/run-pass/libc.rs @@ -17,7 +17,7 @@ fn test_posix_fadvise() { use std::io::Write; use std::os::unix::io::AsRawFd; - let path = tmp().join("miri_test_libc.txt"); + let path = tmp().join("miri_test_libc_posix_fadvise.txt"); // Cleanup before test remove_file(&path).ok(); @@ -40,6 +40,37 @@ fn test_posix_fadvise() { assert_eq!(result, 0); } +#[cfg(target_os = "linux")] +fn test_sync_file_range() { + use std::fs::{remove_file, File}; + use std::io::Write; + use std::os::unix::io::AsRawFd; + + let path = tmp().join("miri_test_libc_sync_file_range.txt"); + // Cleanup before test + remove_file(&path).ok(); + + // Write to a file + let mut file = File::create(&path).unwrap(); + let bytes = b"Hello, World!\n"; + file.write(bytes).unwrap(); + + // Test calling sync_file_range on a file. + let result = unsafe { + libc::sync_file_range( + file.as_raw_fd(), + 0, + 0, + libc::SYNC_FILE_RANGE_WAIT_BEFORE + | libc::SYNC_FILE_RANGE_WRITE + | libc::SYNC_FILE_RANGE_WAIT_AFTER, + ) + }; + drop(file); + remove_file(&path).unwrap(); + assert_eq!(result, 0); +} + fn test_mutex_libc_init_recursive() { unsafe { let mut attr: libc::pthread_mutexattr_t = std::mem::zeroed(); @@ -169,6 +200,9 @@ fn main() { #[cfg(target_os = "linux")] test_posix_fadvise(); + #[cfg(target_os = "linux")] + test_sync_file_range(); + test_mutex_libc_init_recursive(); test_mutex_libc_init_normal(); test_mutex_libc_init_errorcheck();