Skip to content

Commit

Permalink
Auto merge of #1143 - christianpoveda:symlink-shim, r=RalfJung
Browse files Browse the repository at this point in the history
Add shim for symbolic link creation

r? @RalfJung
  • Loading branch information
bors committed Jan 10, 2020
2 parents a91f379 + 5e71f2d commit b4c54b4
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 5 deletions.
10 changes: 10 additions & 0 deletions src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,11 +494,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}

"symlink" => {
let result = this.symlink(args[0], args[1])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}

"stat$INODE64" => {
let result = this.stat(args[0], args[1])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}

"lstat$INODE64" => {
let result = this.lstat(args[0], args[1])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
}

"clock_gettime" => {
let result = this.clock_gettime(args[0], args[1])?;
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
Expand Down
59 changes: 55 additions & 4 deletions src/shims/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,24 +276,76 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.try_unwrap_io_result(result)
}

fn symlink(
&mut self,
target_op: OpTy<'tcx, Tag>,
linkpath_op: OpTy<'tcx, Tag>
) -> InterpResult<'tcx, i32> {
#[cfg(target_family = "unix")]
fn create_link(src: PathBuf, dst: PathBuf) -> std::io::Result<()> {
std::os::unix::fs::symlink(src, dst)
}

#[cfg(target_family = "windows")]
fn create_link(src: PathBuf, dst: PathBuf) -> std::io::Result<()> {
use std::os::windows::fs;
if src.is_dir() {
fs::symlink_dir(src, dst)
} else {
fs::symlink_file(src, dst)
}
}

let this = self.eval_context_mut();

this.check_no_isolation("symlink")?;

let target = this.read_os_str_from_c_str(this.read_scalar(target_op)?.not_undef()?)?.into();
let linkpath = this.read_os_str_from_c_str(this.read_scalar(linkpath_op)?.not_undef()?)?.into();

this.try_unwrap_io_result(create_link(target, linkpath).map(|_| 0))
}

fn stat(
&mut self,
path_op: OpTy<'tcx, Tag>,
buf_op: OpTy<'tcx, Tag>,
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
this.check_no_isolation("stat")?;
// `stat` always follows symlinks.
this.stat_or_lstat(true, path_op, buf_op)
}

// `lstat` is used to get symlink metadata.
fn lstat(
&mut self,
path_op: OpTy<'tcx, Tag>,
buf_op: OpTy<'tcx, Tag>,
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
this.check_no_isolation("lstat")?;
this.stat_or_lstat(false, path_op, buf_op)
}

fn 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();

if this.tcx.sess.target.target.target_os.to_lowercase() != "macos" {
throw_unsup_format!("The `stat` shim is only available for `macos` targets.")
throw_unsup_format!("The `stat` and `lstat` shims are only available for `macos` targets.")
}

let path_scalar = this.read_scalar(path_op)?.not_undef()?;
let path: PathBuf = this.read_os_str_from_c_str(path_scalar)?.into();

let buf = this.deref_operand(buf_op)?;

// `stat` always follows symlinks. `lstat` is used to get symlink metadata.
let metadata = match FileMetadata::new(this, path, true)? {
let metadata = match FileMetadata::new(this, path, follow_symlink)? {
Some(metadata) => metadata,
None => return Ok(-1),
};
Expand Down Expand Up @@ -545,7 +597,6 @@ impl FileMetadata {
let metadata = if follow_symlink {
std::fs::metadata(path)
} else {
// FIXME: metadata for symlinks need testing.
std::fs::symlink_metadata(path)
};

Expand Down
20 changes: 19 additions & 1 deletion tests/run-pass/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ fn main() {
let tmp = std::env::temp_dir();
let filename = PathBuf::from("miri_test_fs.txt");
let path = tmp.join(&filename);
let symlink_path = tmp.join("miri_test_fs_symlink.txt");
let bytes = b"Hello, World!\n";
// Clean the paths for robustness.
remove_file(&path).ok();
remove_file(&symlink_path).ok();

// Test creating, writing and closing a file (closing is tested when `file` is dropped).
let mut file = File::create(&path).unwrap();
Expand All @@ -39,9 +43,23 @@ fn main() {
// Test that metadata of an absolute path is correct.
test_metadata(bytes, &path).unwrap();
// Test that metadata of a relative path is correct.
std::env::set_current_dir(tmp).unwrap();
std::env::set_current_dir(&tmp).unwrap();
test_metadata(bytes, &filename).unwrap();

// Creating a symbolic link should succeed.
std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
// Test that the symbolic link has the same contents as the file.
let mut symlink_file = File::open(&symlink_path).unwrap();
let mut contents = Vec::new();
symlink_file.read_to_end(&mut contents).unwrap();
assert_eq!(bytes, contents.as_slice());
// Test that metadata of a symbolic link is correct.
test_metadata(bytes, &symlink_path).unwrap();
// Test that the metadata of a symbolic link is correct when not following it.
assert!(symlink_path.symlink_metadata().unwrap().file_type().is_symlink());
// Removing symbolic link should succeed.
remove_file(&symlink_path).unwrap();

// Removing file should succeed.
remove_file(&path).unwrap();

Expand Down

0 comments on commit b4c54b4

Please sign in to comment.