Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,9 +494,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
Err(_) => -1,
}
} else {
eprintln!("Miri: Ignored output to FD {}", fd);
// Pretend it all went well.
n as i64
this.write(args[0], args[1], args[2])?
};
// Now, `result` is the value we return back to the program.
this.write_scalar(
Expand Down
72 changes: 61 additions & 11 deletions src/shims/io.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::fs::{ File, OpenOptions };
use std::io::{ Read, Write };

use rustc::ty::layout::Size;

Expand Down Expand Up @@ -42,16 +42,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx

let flag = this.read_scalar(flag_op)?.to_i32()?;

if flag != this.eval_libc_i32("O_RDONLY")? && flag != this.eval_libc_i32("O_CLOEXEC")? {
throw_unsup_format!("Unsupported flag {:#x}", flag);
let mut options = OpenOptions::new();

// The first two bits of the flag correspond to the access mode of the file in linux.
let access_mode = flag & 0b11;

if access_mode == this.eval_libc_i32("O_RDONLY")? {
options.read(true);
} else if access_mode == this.eval_libc_i32("O_WRONLY")? {
options.write(true);
} else if access_mode == this.eval_libc_i32("O_RDWR")? {
options.read(true).write(true);
} else {
throw_unsup_format!("Unsupported access mode {:#x}", access_mode);
}

if flag & this.eval_libc_i32("O_APPEND")? != 0 {
options.append(true);
}
if flag & this.eval_libc_i32("O_TRUNC")? != 0 {
options.truncate(true);
}
if flag & this.eval_libc_i32("O_CREAT")? != 0 {
options.create(true);
}

let path_bytes = this
.memory()
.read_c_str(this.read_scalar(path_op)?.not_undef()?)?;
let path = std::str::from_utf8(path_bytes)
.map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", path_bytes))?;
let fd = File::open(path).map(|file| {

let fd = options.open(path).map(|file| {
let mut fh = &mut this.machine.file_handler;
fh.low += 1;
fh.handles.insert(fh.low, FileHandle { file, flag });
Expand All @@ -70,7 +92,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let this = self.eval_context_mut();

if !this.machine.communicate {
throw_unsup_format!("`open` not available when isolation is enabled")
throw_unsup_format!("`fcntl` not available when isolation is enabled")
}

let fd = this.read_scalar(fd_op)?.to_i32()?;
Expand Down Expand Up @@ -103,7 +125,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let this = self.eval_context_mut();

if !this.machine.communicate {
throw_unsup_format!("`open` not available when isolation is enabled")
throw_unsup_format!("`close` not available when isolation is enabled")
}

let fd = this.read_scalar(fd_op)?.to_i32()?;
Expand All @@ -123,7 +145,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let this = self.eval_context_mut();

if !this.machine.communicate {
throw_unsup_format!("`open` not available when isolation is enabled")
throw_unsup_format!("`read` not available when isolation is enabled")
}

let tcx = &{ this.tcx.tcx };
Expand Down Expand Up @@ -151,10 +173,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
)
}

fn write(
&mut self,
fd_op: OpTy<'tcx, Tag>,
buf_op: OpTy<'tcx, Tag>,
count_op: OpTy<'tcx, Tag>,
) -> InterpResult<'tcx, i64> {
let this = self.eval_context_mut();

if !this.machine.communicate {
throw_unsup_format!("`write` not available when isolation is enabled")
}

let tcx = &{ this.tcx.tcx };

let fd = this.read_scalar(fd_op)?.to_i32()?;
let buf = this.force_ptr(this.read_scalar(buf_op)?.not_undef()?)?;
let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;

// `to_vec` is needed to avoid borrowing issues when writing to the file.
let bytes = this.memory().get(buf.alloc_id)?.get_bytes(tcx, buf, Size::from_bytes(count))?.to_vec();

this.remove_handle_and(fd, |mut handle, this| {
let bytes = handle.file.write(&bytes).map(|bytes| bytes as i64);
this.machine.file_handler.handles.insert(fd, handle);
this.consume_result(bytes)
})
}

/// Helper function that gets a `FileHandle` immutable reference and allows to manipulate it
/// using `f`.
/// using the `f` closure.
///
/// If the `fd` file descriptor does not corresponds to a file, this functions returns `Ok(-1)`
/// If the `fd` file descriptor does not correspond to a file, this functions returns `Ok(-1)`
/// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
///
/// This function uses `T: From<i32>` instead of `i32` directly because some IO related
Expand All @@ -177,7 +227,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
/// to modify `MiriEvalContext` at the same time, so you can modify the handle and reinsert it
/// using `f`.
///
/// If the `fd` file descriptor does not corresponds to a file, this functions returns `Ok(-1)`
/// If the `fd` file descriptor does not correspond to a file, this functions returns `Ok(-1)`
/// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
///
/// This function uses `T: From<i32>` instead of `i32` directly because some IO related
Expand Down
1 change: 0 additions & 1 deletion tests/hello.txt

This file was deleted.

10 changes: 8 additions & 2 deletions tests/run-pass/file_read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@
// compile-flags: -Zmiri-disable-isolation

use std::fs::File;
use std::io::Read;
use std::io::{ Read, Write };

fn main() {
// FIXME: create the file and delete it when `rm` is implemented.
// FIXME: remove the file and delete it when `rm` is implemented.

// Test creating, writing and closing a file (closing is tested when `file` is dropped).
let mut file = File::create("./tests/hello.txt").unwrap();
file.write(b"Hello, World!\n").unwrap();

// Test opening, reading and closing a file.
let mut file = File::open("./tests/hello.txt").unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
Expand Down