Auto merge of #1143 - christianpoveda:symlink-shim, r=RalfJung

Add shim for symbolic link creation

r? @RalfJung
This commit is contained in:
bors 2020-01-10 17:21:12 +00:00
commit b4c54b4641
3 changed files with 84 additions and 5 deletions

View File

@ -494,11 +494,21 @@ fn emulate_foreign_item(
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)?;

View File

@ -276,15 +276,68 @@ fn unlink(&mut self, path_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
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()?;
@ -292,8 +345,7 @@ fn stat(
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),
};
@ -545,7 +597,6 @@ fn new<'tcx, 'mir>(
let metadata = if follow_symlink {
std::fs::metadata(path)
} else {
// FIXME: metadata for symlinks need testing.
std::fs::symlink_metadata(path)
};

View File

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