Add file sync shims

Adds implementations for fsync, fdatasync, and sync_file_range
This commit is contained in:
David Cook 2020-04-21 22:01:40 -05:00
parent ac09e2f5cd
commit 87c4694448
5 changed files with 134 additions and 1 deletions

View File

@ -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" => {

View File

@ -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" => {

View File

@ -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

View File

@ -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);

View File

@ -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();