Auto merge of #1838 - atsmtat:fs-isolation, r=RalfJung

Fix use of deprecated `check_no_isolation` in posix fs shims

Update posix fs shims to use new API `reject_in_isolation`, which
allows rejection with error code instead of always forcing abort.
Error code chosen for each op is the most appropriate one from the
list in corresponding syscall's manual.

Updated helper APIs to not use quotes (\`) around input name while
preparing the message. This allows callers to pass multi-word string
like -- "\`read\` from stdin".

Cc https://github.com/rust-lang/miri/issues/1034
This commit is contained in:
bors 2021-07-25 09:34:02 +00:00
commit 1677946d74
6 changed files with 260 additions and 66 deletions

View File

@ -328,7 +328,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
CreatedAlloc(AllocId(id)) => format!("created allocation with id {}", id),
FreedAlloc(AllocId(id)) => format!("freed allocation with id {}", id),
RejectedIsolatedOp(ref op) =>
format!("`{}` was made to return an error due to isolation", op),
format!("{} was made to return an error due to isolation", op),
};
let (title, diag_level) = match e {

View File

@ -409,7 +409,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
RejectOpWith::WarningWithoutBacktrace => {
this.tcx
.sess
.warn(&format!("`{}` was made to return an error due to isolation", op_name));
.warn(&format!("{} was made to return an error due to isolation", op_name));
Ok(())
}
RejectOpWith::Warning => {

View File

@ -322,7 +322,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let size = this.read_scalar(&size_op)?.to_machine_usize(&*this.tcx)?;
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("getcwd", reject_with)?;
this.reject_in_isolation("`getcwd`", reject_with)?;
this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
return Ok(Pointer::null());
}
@ -355,7 +355,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let buf = this.read_pointer(buf_op)?;
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("GetCurrentDirectoryW", reject_with)?;
this.reject_in_isolation("`GetCurrentDirectoryW`", reject_with)?;
this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
return Ok(0);
}
@ -380,7 +380,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?;
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("chdir", reject_with)?;
this.reject_in_isolation("`chdir`", reject_with)?;
this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
return Ok(-1);
@ -408,7 +408,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let path = this.read_path_from_wide_str(this.read_pointer(path_op)?)?;
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("SetCurrentDirectoryW", reject_with)?;
this.reject_in_isolation("`SetCurrentDirectoryW`", reject_with)?;
this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
return Ok(0);

View File

@ -4,7 +4,7 @@ use std::convert::{TryFrom, TryInto};
use std::fs::{
read_dir, remove_dir, remove_file, rename, DirBuilder, File, FileType, OpenOptions, ReadDir,
};
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write};
use std::path::Path;
use std::time::SystemTime;
@ -304,28 +304,6 @@ impl<'tcx> FileHandler {
impl<'mir, 'tcx: 'mir> EvalContextExtPrivate<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
/// Emulate `stat` or `lstat` on `macos`. This function is not intended to be
/// called directly from `emulate_foreign_item_by_name`, so it does not check if isolation is
/// disabled or if the target OS is the correct one. Please use `macos_stat` or
/// `macos_lstat` instead.
fn macos_stat_or_lstat(
&mut self,
follow_symlink: bool,
path_op: &OpTy<'tcx, Tag>,
buf_op: &OpTy<'tcx, Tag>,
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let path_scalar = this.read_pointer(path_op)?;
let path = this.read_path_from_c_str(path_scalar)?.into_owned();
let metadata = match FileMetadata::from_path(this, &path, follow_symlink)? {
Some(metadata) => metadata,
None => return Ok(-1),
};
this.macos_stat_write_buf(metadata, buf_op)
}
fn macos_stat_write_buf(
&mut self,
metadata: FileMetadata,
@ -504,8 +482,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
this.check_no_isolation("`open`")?;
let flag = this.read_scalar(flag_op)?.to_i32()?;
// Get the mode. On macOS, the argument type `mode_t` is actually `u16`, but
@ -583,6 +559,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`open`", reject_with)?;
this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
return Ok(-1);
}
let fd = options.open(&path).map(|file| {
let fh = &mut this.machine.file_handler;
fh.insert_fd(Box::new(FileHandle { file, writable }))
@ -594,8 +577,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
fn fcntl(&mut self, args: &[OpTy<'tcx, Tag>]) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
this.check_no_isolation("`fcntl`")?;
if args.len() < 2 {
throw_ub_format!(
"incorrect number of arguments for fcntl: got {}, expected at least 2",
@ -604,6 +585,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
}
let fd = this.read_scalar(&args[0])?.to_i32()?;
let cmd = this.read_scalar(&args[1])?.to_i32()?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`fcntl`", reject_with)?;
this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
return Ok(-1);
}
// 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
@ -785,10 +774,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
fn unlink(&mut self, path_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
this.check_no_isolation("`unlink`")?;
let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`unlink`", reject_with)?;
this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
return Ok(-1);
}
let result = remove_file(path).map(|_| 0);
this.try_unwrap_io_result(result)
}
@ -810,12 +804,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
}
let this = self.eval_context_mut();
this.check_no_isolation("`symlink`")?;
let target = this.read_path_from_c_str(this.read_pointer(target_op)?)?;
let linkpath = this.read_path_from_c_str(this.read_pointer(linkpath_op)?)?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`symlink`", reject_with)?;
this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
return Ok(-1);
}
let result = create_link(&target, &linkpath).map(|_| 0);
this.try_unwrap_io_result(result)
}
@ -827,9 +825,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
this.assert_target_os("macos", "stat");
this.check_no_isolation("`stat`")?;
let path_scalar = this.read_pointer(path_op)?;
let path = this.read_path_from_c_str(path_scalar)?.into_owned();
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`stat`", reject_with)?;
let eacc = this.eval_libc("EACCES")?;
this.set_last_error(eacc)?;
return Ok(-1);
}
// `stat` always follows symlinks.
this.macos_stat_or_lstat(true, path_op, buf_op)
let metadata = match FileMetadata::from_path(this, &path, true)? {
Some(metadata) => metadata,
None => return Ok(-1),
};
this.macos_stat_write_buf(metadata, buf_op)
}
// `lstat` is used to get symlink metadata.
@ -840,8 +854,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
this.assert_target_os("macos", "lstat");
this.check_no_isolation("`lstat`")?;
this.macos_stat_or_lstat(false, path_op, buf_op)
let path_scalar = this.read_pointer(path_op)?;
let path = this.read_path_from_c_str(path_scalar)?.into_owned();
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`lstat`", reject_with)?;
let eacc = this.eval_libc("EACCES")?;
this.set_last_error(eacc)?;
return Ok(-1);
}
let metadata = match FileMetadata::from_path(this, &path, false)? {
Some(metadata) => metadata,
None => return Ok(-1),
};
this.macos_stat_write_buf(metadata, buf_op)
}
fn macos_fstat(
@ -852,10 +882,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let this = self.eval_context_mut();
this.assert_target_os("macos", "fstat");
this.check_no_isolation("`fstat`")?;
let fd = this.read_scalar(fd_op)?.to_i32()?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`fstat`", reject_with)?;
// Set error code as "EBADF" (bad fd)
return this.handle_not_found();
}
let metadata = match FileMetadata::from_fd(this, fd)? {
Some(metadata) => metadata,
None => return Ok(-1),
@ -874,7 +910,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let this = self.eval_context_mut();
this.assert_target_os("linux", "statx");
this.check_no_isolation("`statx`")?;
let statxbuf_ptr = this.read_pointer(statxbuf_op)?;
let pathname_ptr = this.read_pointer(pathname_op)?;
@ -924,6 +959,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
)
}
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`statx`", reject_with)?;
let ecode = if path.is_absolute() || dirfd == this.eval_libc_i32("AT_FDCWD")? {
// since `path` is provided, either absolute or
// relative to CWD, `EACCES` is the most relevant.
this.eval_libc("EACCES")?
} else {
// `dirfd` is set to target file, and `path` is empty
// (or we would have hit the `throw_unsup_format`
// above). `EACCES` would violate the spec.
assert!(empty_path_flag);
this.eval_libc("EBADF")?
};
this.set_last_error(ecode)?;
return Ok(-1);
}
// the `_mask_op` paramter specifies the file information that the caller requested.
// However `statx` is allowed to return information that was not requested or to not
// return information that was requested. This `mask` represents the information we can
@ -1032,8 +1085,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
this.check_no_isolation("`rename`")?;
let oldpath_ptr = this.read_pointer(oldpath_op)?;
let newpath_ptr = this.read_pointer(newpath_op)?;
@ -1046,6 +1097,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let oldpath = this.read_path_from_c_str(oldpath_ptr)?;
let newpath = this.read_path_from_c_str(newpath_ptr)?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`rename`", reject_with)?;
this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
return Ok(-1);
}
let result = rename(oldpath, newpath).map(|_| 0);
this.try_unwrap_io_result(result)
@ -1058,8 +1116,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
this.check_no_isolation("`mkdir`")?;
#[cfg_attr(not(unix), allow(unused_variables))]
let mode = if this.tcx.sess.target.os == "macos" {
u32::from(this.read_scalar(mode_op)?.to_u16()?)
@ -1069,6 +1125,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`mkdir`", reject_with)?;
this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
return Ok(-1);
}
#[cfg_attr(not(unix), allow(unused_mut))]
let mut builder = DirBuilder::new();
@ -1088,10 +1151,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
fn rmdir(&mut self, path_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
this.check_no_isolation("`rmdir`")?;
let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`rmdir`", reject_with)?;
this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
return Ok(-1);
}
let result = remove_dir(path).map(|_| 0i32);
this.try_unwrap_io_result(result)
@ -1100,10 +1168,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
fn opendir(&mut self, name_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, Scalar<Tag>> {
let this = self.eval_context_mut();
this.check_no_isolation("`opendir`")?;
let name = this.read_path_from_c_str(this.read_pointer(name_op)?)?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`opendir`", reject_with)?;
let eacc = this.eval_libc("EACCES")?;
this.set_last_error(eacc)?;
return Ok(Scalar::null_ptr(this));
}
let result = read_dir(name);
match result {
@ -1131,10 +1205,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let this = self.eval_context_mut();
this.assert_target_os("linux", "readdir64_r");
this.check_no_isolation("`readdir64_r`")?;
let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`readdir64_r`", reject_with)?;
// Set error code as "EBADF" (bad fd)
return this.handle_not_found();
}
let dir_iter = this.machine.dir_handler.streams.get_mut(&dirp).ok_or_else(|| {
err_unsup_format!("the DIR pointer passed to readdir64_r did not come from opendir")
})?;
@ -1224,10 +1304,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let this = self.eval_context_mut();
this.assert_target_os("macos", "readdir_r");
this.check_no_isolation("`readdir_r`")?;
let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`readdir_r`", reject_with)?;
// Set error code as "EBADF" (bad fd)
return this.handle_not_found();
}
let dir_iter = this.machine.dir_handler.streams.get_mut(&dirp).ok_or_else(|| {
err_unsup_format!("the DIR pointer passed to readdir_r did not come from opendir")
})?;
@ -1313,10 +1399,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
fn closedir(&mut self, dirp_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
this.check_no_isolation("`closedir`")?;
let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`closedir`", reject_with)?;
// Set error code as "EBADF" (bad fd)
return this.handle_not_found();
}
if let Some(dir_iter) = this.machine.dir_handler.streams.remove(&dirp) {
drop(dir_iter);
Ok(0)
@ -1332,10 +1423,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
this.check_no_isolation("`ftruncate64`")?;
let fd = this.read_scalar(fd_op)?.to_i32()?;
let length = this.read_scalar(length_op)?.to_i64()?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`ftruncate64`", reject_with)?;
// Set error code as "EBADF" (bad fd)
return this.handle_not_found();
}
if let Some(file_descriptor) = this.machine.file_handler.handles.get_mut(&fd) {
// FIXME: Support ftruncate64 for all FDs
let FileHandle { file, writable } = file_descriptor.as_file_handle()?;
@ -1367,9 +1464,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let this = self.eval_context_mut();
this.check_no_isolation("`fsync`")?;
let fd = this.read_scalar(fd_op)?.to_i32()?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`fsync`", reject_with)?;
// Set error code as "EBADF" (bad fd)
return this.handle_not_found();
}
if let Some(file_descriptor) = this.machine.file_handler.handles.get(&fd) {
// FIXME: Support fsync for all FDs
let FileHandle { file, writable } = file_descriptor.as_file_handle()?;
@ -1383,9 +1486,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
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()?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`fdatasync`", reject_with)?;
// Set error code as "EBADF" (bad fd)
return this.handle_not_found();
}
if let Some(file_descriptor) = this.machine.file_handler.handles.get(&fd) {
// FIXME: Support fdatasync for all FDs
let FileHandle { file, writable } = file_descriptor.as_file_handle()?;
@ -1405,8 +1514,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
) -> 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()?;
@ -1426,6 +1533,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
return Ok(-1);
}
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`sync_file_range`", reject_with)?;
// Set error code as "EBADF" (bad fd)
return this.handle_not_found();
}
if let Some(file_descriptor) = this.machine.file_handler.handles.get(&fd) {
// FIXME: Support sync_data_range for all FDs
let FileHandle { file, writable } = file_descriptor.as_file_handle()?;
@ -1444,12 +1558,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
) -> InterpResult<'tcx, i64> {
let this = self.eval_context_mut();
this.check_no_isolation("readlink")?;
let pathname = this.read_path_from_c_str(this.read_pointer(pathname_op)?)?;
let buf = this.read_pointer(buf_op)?;
let bufsize = this.read_scalar(bufsize_op)?.to_machine_usize(this)?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`readlink`", reject_with)?;
let eacc = this.eval_libc("EACCES")?;
this.set_last_error(eacc)?;
return Ok(-1);
}
let result = std::fs::read_link(pathname);
match result {
Ok(resolved) => {

View File

@ -0,0 +1,54 @@
// ignore-windows: File handling is not implemented yet
// compile-flags: -Zmiri-isolation-error=warn-nobacktrace
// normalize-stderr-test "(stat(x)?)" -> "$$STAT"
#![feature(rustc_private)]
extern crate libc;
use std::ffi::CString;
use std::os::unix;
use std::fs::{self, File};
use std::io::{Error, ErrorKind};
fn main() {
// test `open`
assert_eq!(File::create("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
// test `fcntl`
unsafe {
assert_eq!(libc::fcntl(1, libc::F_DUPFD, 0), -1);
assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EPERM));
}
// test `unlink`
assert_eq!(fs::remove_file("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
// test `symlink`
assert_eq!(unix::fs::symlink("foo.txt", "foo_link.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
// test `readlink`
let symlink_c_str = CString::new("foo.txt").unwrap();
let mut buf = vec![0; "foo_link.txt".len() + 1];
unsafe {
assert_eq!(libc::readlink(symlink_c_str.as_ptr(), buf.as_mut_ptr(), buf.len()), -1);
assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES));
}
// test `stat`
assert_eq!(fs::metadata("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES));
// test `rename`
assert_eq!(fs::rename("a.txt", "b.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
// test `mkdir`
assert_eq!(fs::create_dir("foo/bar").unwrap_err().kind(), ErrorKind::PermissionDenied);
// test `rmdir`
assert_eq!(fs::remove_dir("foo/bar").unwrap_err().kind(), ErrorKind::PermissionDenied);
// test `opendir`
assert_eq!(fs::read_dir("foo/bar").unwrap_err().kind(), ErrorKind::PermissionDenied);
assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES));
}

View File

@ -0,0 +1,20 @@
warning: `open` was made to return an error due to isolation
warning: `fcntl` was made to return an error due to isolation
warning: `unlink` was made to return an error due to isolation
warning: `symlink` was made to return an error due to isolation
warning: `readlink` was made to return an error due to isolation
warning: `$STAT` was made to return an error due to isolation
warning: `rename` was made to return an error due to isolation
warning: `mkdir` was made to return an error due to isolation
warning: `rmdir` was made to return an error due to isolation
warning: `opendir` was made to return an error due to isolation