Skip to content

Commit 87c4694

Browse files
committed
Add file sync shims
Adds implementations for fsync, fdatasync, and sync_file_range
1 parent ac09e2f commit 87c4694

File tree

5 files changed

+134
-1
lines changed

5 files changed

+134
-1
lines changed

src/shims/foreign_items/posix.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
136136
// "lseek" is only used on macOS which is 64bit-only, so `i64` always works.
137137
this.write_scalar(Scalar::from_i64(result), dest)?;
138138
}
139+
"fsync" => {
140+
let result = this.fsync(args[0])?;
141+
this.write_scalar(Scalar::from_i32(result), dest)?;
142+
}
143+
"fdatasync" => {
144+
let result = this.fdatasync(args[0])?;
145+
this.write_scalar(Scalar::from_i32(result), dest)?;
146+
}
139147

140148
// Allocation
141149
"posix_memalign" => {

src/shims/foreign_items/posix/linux.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
5454
// fadvise is only informational, we can ignore it.
5555
this.write_null(dest)?;
5656
}
57+
// Linux-only
58+
"sync_file_range" => {
59+
let result = this.sync_file_range(args[0], args[1], args[2], args[3])?;
60+
this.write_scalar(Scalar::from_i32(result), dest)?;
61+
}
5762

5863
// Time related shims
5964
"clock_gettime" => {

src/shims/fs.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
375375
fh.insert_fd_with_min_fd(FileHandle { file: duplicated, writable }, start)
376376
});
377377
this.try_unwrap_io_result(fd_result)
378+
} else if this.tcx.sess.target.target.target_os == "macos"
379+
&& cmd == this.eval_libc_i32("F_FULLFSYNC")?
380+
{
381+
if let Some(FileHandle { file, writable: _ }) = this.machine.file_handler.handles.get_mut(&fd) {
382+
let result = file.sync_all();
383+
this.try_unwrap_io_result(result.map(|_| 0i32))
384+
} else {
385+
this.handle_not_found()
386+
}
378387
} else {
379388
throw_unsup_format!("the {:#x} command is not supported for `fcntl`)", cmd);
380389
}
@@ -1103,6 +1112,59 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
11031112
this.handle_not_found()
11041113
}
11051114
}
1115+
1116+
fn fsync(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
1117+
let this = self.eval_context_mut();
1118+
1119+
this.check_no_isolation("fsync")?;
1120+
1121+
let fd = this.read_scalar(fd_op)?.to_i32()?;
1122+
if let Some(FileHandle { file, writable: _ }) = this.machine.file_handler.handles.get_mut(&fd) {
1123+
let result = file.sync_all();
1124+
this.try_unwrap_io_result(result.map(|_| 0i32))
1125+
} else {
1126+
this.handle_not_found()
1127+
}
1128+
}
1129+
1130+
fn fdatasync(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
1131+
let this = self.eval_context_mut();
1132+
1133+
this.check_no_isolation("fdatasync")?;
1134+
1135+
let fd = this.read_scalar(fd_op)?.to_i32()?;
1136+
if let Some(FileHandle { file, writable: _ }) = this.machine.file_handler.handles.get_mut(&fd) {
1137+
let result = file.sync_data();
1138+
this.try_unwrap_io_result(result.map(|_| 0i32))
1139+
} else {
1140+
this.handle_not_found()
1141+
}
1142+
}
1143+
1144+
fn sync_file_range(
1145+
&mut self,
1146+
fd_op: OpTy<'tcx, Tag>,
1147+
offset_op: OpTy<'tcx, Tag>,
1148+
nbytes_op: OpTy<'tcx, Tag>,
1149+
flags_op: OpTy<'tcx, Tag>,
1150+
) -> InterpResult<'tcx, i32> {
1151+
let this = self.eval_context_mut();
1152+
1153+
this.check_no_isolation("sync_file_range")?;
1154+
1155+
let fd = this.read_scalar(fd_op)?.to_i32()?;
1156+
let _offset = this.read_scalar(offset_op)?.to_i64()?;
1157+
let _nbytes = this.read_scalar(nbytes_op)?.to_i64()?;
1158+
let _flags = this.read_scalar(flags_op)?.to_u32()?;
1159+
if let Some(FileHandle { file, writable: _ }) = this.machine.file_handler.handles.get_mut(&fd) {
1160+
// In the interest of host compatibility, we conservatively ignore
1161+
// offset, nbytes, and flags, and sync the entire file.
1162+
let result = file.sync_data();
1163+
this.try_unwrap_io_result(result.map(|_| 0i32))
1164+
} else {
1165+
this.handle_not_found()
1166+
}
1167+
}
11061168
}
11071169

11081170
/// Extracts the number of seconds and nanoseconds elapsed between `time` and the unix epoch when

tests/run-pass/fs.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ fn main() {
1414
test_seek();
1515
test_metadata();
1616
test_file_set_len();
17+
test_file_sync_all();
18+
test_file_sync_data();
1719
test_symlink();
1820
test_errors();
1921
test_rename();
@@ -182,6 +184,28 @@ fn test_file_set_len() {
182184
remove_file(&path).unwrap();
183185
}
184186

187+
fn test_file_sync_all() {
188+
let bytes = b"Hello, World!\n";
189+
let path = prepare_with_content("miri_test_fs_sync_all.txt", bytes);
190+
191+
// Test that we can call sync_all (can't readily test effects of this operation)
192+
let file = File::open(&path).unwrap();
193+
file.sync_all().unwrap();
194+
195+
remove_file(&path).unwrap();
196+
}
197+
198+
fn test_file_sync_data() {
199+
let bytes = b"Hello, World!\n";
200+
let path = prepare_with_content("miri_test_fs_sync_data.txt", bytes);
201+
202+
// Test that we can call sync_data (can't readily test effects of this operation)
203+
let file = File::open(&path).unwrap();
204+
file.sync_data().unwrap();
205+
206+
remove_file(&path).unwrap();
207+
}
208+
185209
fn test_symlink() {
186210
let bytes = b"Hello, World!\n";
187211
let path = prepare_with_content("miri_test_fs_link_target.txt", bytes);

tests/run-pass/libc.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ fn test_posix_fadvise() {
1717
use std::io::Write;
1818
use std::os::unix::io::AsRawFd;
1919

20-
let path = tmp().join("miri_test_libc.txt");
20+
let path = tmp().join("miri_test_libc_posix_fadvise.txt");
2121
// Cleanup before test
2222
remove_file(&path).ok();
2323

@@ -40,6 +40,37 @@ fn test_posix_fadvise() {
4040
assert_eq!(result, 0);
4141
}
4242

43+
#[cfg(target_os = "linux")]
44+
fn test_sync_file_range() {
45+
use std::fs::{remove_file, File};
46+
use std::io::Write;
47+
use std::os::unix::io::AsRawFd;
48+
49+
let path = tmp().join("miri_test_libc_sync_file_range.txt");
50+
// Cleanup before test
51+
remove_file(&path).ok();
52+
53+
// Write to a file
54+
let mut file = File::create(&path).unwrap();
55+
let bytes = b"Hello, World!\n";
56+
file.write(bytes).unwrap();
57+
58+
// Test calling sync_file_range on a file.
59+
let result = unsafe {
60+
libc::sync_file_range(
61+
file.as_raw_fd(),
62+
0,
63+
0,
64+
libc::SYNC_FILE_RANGE_WAIT_BEFORE
65+
| libc::SYNC_FILE_RANGE_WRITE
66+
| libc::SYNC_FILE_RANGE_WAIT_AFTER,
67+
)
68+
};
69+
drop(file);
70+
remove_file(&path).unwrap();
71+
assert_eq!(result, 0);
72+
}
73+
4374
fn test_mutex_libc_init_recursive() {
4475
unsafe {
4576
let mut attr: libc::pthread_mutexattr_t = std::mem::zeroed();
@@ -169,6 +200,9 @@ fn main() {
169200
#[cfg(target_os = "linux")]
170201
test_posix_fadvise();
171202

203+
#[cfg(target_os = "linux")]
204+
test_sync_file_range();
205+
172206
test_mutex_libc_init_recursive();
173207
test_mutex_libc_init_normal();
174208
test_mutex_libc_init_errorcheck();

0 commit comments

Comments
 (0)