Skip to content

Commit cd495cb

Browse files
committed
Add file writing capabilities
1 parent 1037f69 commit cd495cb

File tree

4 files changed

+65
-12
lines changed

4 files changed

+65
-12
lines changed

src/shims/foreign_items.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -494,9 +494,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
494494
Err(_) => -1,
495495
}
496496
} else {
497-
eprintln!("Miri: Ignored output to FD {}", fd);
498-
// Pretend it all went well.
499-
n as i64
497+
this.write(args[0], args[1], args[2])?
500498
};
501499
// Now, `result` is the value we return back to the program.
502500
this.write_scalar(

src/shims/io.rs

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use 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

55
use 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 });
@@ -151,8 +173,36 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
151173
)
152174
}
153175

176+
fn write(
177+
&mut self,
178+
fd_op: OpTy<'tcx, Tag>,
179+
buf_op: OpTy<'tcx, Tag>,
180+
count_op: OpTy<'tcx, Tag>,
181+
) -> InterpResult<'tcx, i64> {
182+
let this = self.eval_context_mut();
183+
184+
if !this.machine.communicate {
185+
throw_unsup_format!("`write` not available when isolation is enabled")
186+
}
187+
188+
let tcx = &{ this.tcx.tcx };
189+
190+
let fd = this.read_scalar(fd_op)?.to_i32()?;
191+
let buf = this.force_ptr(this.read_scalar(buf_op)?.not_undef()?)?;
192+
let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;
193+
194+
// `to_vec` is needed to avoid borrowing issues when writing to the file.
195+
let bytes = this.memory().get(buf.alloc_id)?.get_bytes(tcx, buf, Size::from_bytes(count))?.to_vec();
196+
197+
this.remove_handle_and(fd, |mut handle, this| {
198+
let bytes = handle.file.write(&bytes).map(|bytes| bytes as i64);
199+
this.machine.file_handler.handles.insert(fd, handle);
200+
this.consume_result(bytes)
201+
})
202+
}
203+
154204
/// Helper function that gets a `FileHandle` immutable reference and allows to manipulate it
155-
/// using `f`.
205+
/// using the `f` closure.
156206
///
157207
/// If the `fd` file descriptor does not corresponds to a file, this functions returns `Ok(-1)`
158208
/// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).

tests/hello.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

tests/run-pass/file_read.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@
22
// compile-flags: -Zmiri-disable-isolation
33

44
use std::fs::File;
5-
use std::io::Read;
5+
use std::io::{ Read, Write };
66

77
fn main() {
8-
// FIXME: create the file and delete it when `rm` is implemented.
8+
// FIXME: remove the file and delete it when `rm` is implemented.
9+
10+
// Test creating, writing and closing a file (closing is tested when `file` is dropped).
11+
let mut file = File::create("./tests/hello.txt").unwrap();
12+
file.write(b"Hello, World!\n").unwrap();
13+
14+
// Test opening, reading and closing a file.
915
let mut file = File::open("./tests/hello.txt").unwrap();
1016
let mut contents = String::new();
1117
file.read_to_string(&mut contents).unwrap();

0 commit comments

Comments
 (0)