Skip to content
Merged
Changes from 3 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
66 changes: 37 additions & 29 deletions src/shims/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use crate::*;

pub struct FileHandle {
file: File,
flag: i32,
}

pub struct FileHandler {
Expand Down Expand Up @@ -44,7 +43,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx

let mut options = OpenOptions::new();

// The first two bits of the flag correspond to the access mode of the file in linux.
// The first two bits of the flag correspond to the access mode of the file in linux. This
// is done this way because `O_RDONLY` is zero in several platforms.
let access_mode = flag & 0b11;

if access_mode == this.eval_libc_i32("O_RDONLY")? {
Expand All @@ -56,15 +56,36 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
} else {
throw_unsup_format!("Unsupported access mode {:#x}", access_mode);
}
// We need to check that there aren't unsupported options in `flag`. For this we try to
// reproduce the content of `flag` in the `mirror` variable using only the supported
// options.
let mut mirror = access_mode;

if flag & this.eval_libc_i32("O_APPEND")? != 0 {
let o_append = this.eval_libc_i32("O_APPEND")?;
if flag & o_append != 0 {
options.append(true);
mirror |= o_append;
}
if flag & this.eval_libc_i32("O_TRUNC")? != 0 {
let o_trunc = this.eval_libc_i32("O_TRUNC")?;
if flag & o_trunc != 0 {
options.truncate(true);
mirror |= o_trunc;
}
if flag & this.eval_libc_i32("O_CREAT")? != 0 {
let o_creat = this.eval_libc_i32("O_CREAT")?;
if flag & o_creat != 0 {
options.create(true);
mirror |= o_creat;
}
let o_cloexec = this.eval_libc_i32("O_CLOEXEC")?;
if flag & o_cloexec != 0 {
// We do not need to do anything for this flag because `std` already sets it.
// (Technically we do not support *not* setting this flag, but we ignore that.)
mirror |= o_cloexec;
}
// If `flag` is not equal to `mirror`, there is an unsupported option enabled in `flag`,
// then we throw an error.
if flag != mirror {
throw_unsup_format!("unsupported flags {:#x}", flag & !mirror);
}

let path_bytes = this
Expand All @@ -76,7 +97,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
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 });
fh.handles.insert(fh.low, FileHandle { file });
fh.low
});

Expand All @@ -87,7 +108,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
&mut self,
fd_op: OpTy<'tcx, Tag>,
cmd_op: OpTy<'tcx, Tag>,
arg_op: Option<OpTy<'tcx, Tag>>,
_arg1_op: Option<OpTy<'tcx, Tag>>,
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();

Expand All @@ -97,29 +118,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx

let fd = this.read_scalar(fd_op)?.to_i32()?;
let cmd = this.read_scalar(cmd_op)?.to_i32()?;

if cmd == this.eval_libc_i32("F_SETFD")? {
// This does not affect the file itself. Certain flags might require changing the file
// or the way it is accessed somehow.
let flag = this.read_scalar(arg_op.unwrap())?.to_i32()?;
// The only usage of this in stdlib at the moment is to enable the `FD_CLOEXEC` flag.
let fd_cloexec = this.eval_libc_i32("FD_CLOEXEC")?;
if let Some(FileHandle { flag: old_flag, .. }) =
this.machine.file_handler.handles.get_mut(&fd)
{
// Check that the only difference between the old flag and the current flag is
// exactly the `FD_CLOEXEC` value.
if flag ^ *old_flag == fd_cloexec {
*old_flag = flag;
} else {
throw_unsup_format!("Unsupported arg {:#x} for `F_SETFD`", flag);
}
}
Ok(0)
} else if cmd == this.eval_libc_i32("F_GETFD")? {
this.get_handle_and(fd, |handle| Ok(handle.flag))
// We only support getting the flags for a descriptor
if cmd == this.eval_libc_i32("F_GETFD")? {
// Currently this is the only flag that `F_GETFD` returns. It is OK to just return the
// `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
// always sets this flag when opening a file. However we still need to check that the
// file itself is open.
this.get_handle_and(fd, |_| Ok(0))?;
this.eval_libc_i32("FD_CLOEXEC")
} else {
throw_unsup_format!("Unsupported command {:#x}", cmd);
throw_unsup_format!("The {:#x} command is not supported for `fcntl`)", cmd);
}
}

Expand Down