From 4a1dbc77dc9f64aebd0b16927bb71f3178376d30 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Mon, 6 Jan 2020 16:30:17 -0500 Subject: [PATCH 1/5] Add shim for symbolic link creation --- src/shims/foreign_items.rs | 5 +++++ src/shims/fs.rs | 31 ++++++++++++++++++++++++++++++- tests/run-pass/fs.rs | 17 ++++++++++++++++- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 07e3b7d75826..6a2f42f83215 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -494,6 +494,11 @@ 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)?; diff --git a/src/shims/fs.rs b/src/shims/fs.rs index 8c8bd6f7bb97..c5b753f3b6a2 100644 --- a/src/shims/fs.rs +++ b/src/shims/fs.rs @@ -276,6 +276,36 @@ 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(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>, @@ -545,7 +575,6 @@ impl FileMetadata { let metadata = if follow_symlink { std::fs::metadata(path) } else { - // FIXME: metadata for symlinks need testing. std::fs::symlink_metadata(path) }; diff --git a/tests/run-pass/fs.rs b/tests/run-pass/fs.rs index 85e39bc45112..9e0428fb57c2 100644 --- a/tests/run-pass/fs.rs +++ b/tests/run-pass/fs.rs @@ -39,9 +39,24 @@ 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 + let symlink_path = tmp.join("miri_test_fs_symlink.txt"); + 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(); From 329310fbd63c564df6dd76f40181668861a7ed16 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Tue, 7 Jan 2020 11:09:07 -0500 Subject: [PATCH 2/5] Clean paths for robustness --- tests/run-pass/fs.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/run-pass/fs.rs b/tests/run-pass/fs.rs index 9e0428fb57c2..90b5dcfb5fa6 100644 --- a/tests/run-pass/fs.rs +++ b/tests/run-pass/fs.rs @@ -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).unwrap_or(()); + remove_file(&symlink_path).unwrap_or(()); // Test creating, writing and closing a file (closing is tested when `file` is dropped). let mut file = File::create(&path).unwrap(); @@ -42,8 +46,7 @@ fn main() { std::env::set_current_dir(&tmp).unwrap(); test_metadata(bytes, &filename).unwrap(); - // Creating a symbolic link should succeed - let symlink_path = tmp.join("miri_test_fs_symlink.txt"); + // 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(); From 91cf68fac569d0817fc8649bff28d727bbed47b2 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Tue, 7 Jan 2020 11:29:25 -0500 Subject: [PATCH 3/5] Add lstat shim for macos --- src/shims/foreign_items.rs | 5 +++++ src/shims/fs.rs | 26 ++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 6a2f42f83215..f3baebad143b 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -504,6 +504,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx 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)?; diff --git a/src/shims/fs.rs b/src/shims/fs.rs index c5b753f3b6a2..3927f0e64919 100644 --- a/src/shims/fs.rs +++ b/src/shims/fs.rs @@ -312,6 +312,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx 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.") @@ -322,8 +345,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx 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), }; From 0b5a30515e6977e4a9deaefa1c0ff243afbd6ef6 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Fri, 10 Jan 2020 12:01:05 -0500 Subject: [PATCH 4/5] small corrections --- src/shims/fs.rs | 2 +- tests/run-pass/fs.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/shims/fs.rs b/src/shims/fs.rs index 3927f0e64919..57a88f2ccaae 100644 --- a/src/shims/fs.rs +++ b/src/shims/fs.rs @@ -337,7 +337,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx 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()?; diff --git a/tests/run-pass/fs.rs b/tests/run-pass/fs.rs index 90b5dcfb5fa6..81c56e4aafc7 100644 --- a/tests/run-pass/fs.rs +++ b/tests/run-pass/fs.rs @@ -22,8 +22,8 @@ fn main() { let symlink_path = tmp.join("miri_test_fs_symlink.txt"); let bytes = b"Hello, World!\n"; // Clean the paths for robustness. - remove_file(&path).unwrap_or(()); - remove_file(&symlink_path).unwrap_or(()); + 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(); From 5e71f2debc4df0bd25d29329cd21a7c55c5dc3ba Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Fri, 10 Jan 2020 12:18:24 -0500 Subject: [PATCH 5/5] fix windows symlink creation --- src/shims/fs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shims/fs.rs b/src/shims/fs.rs index 57a88f2ccaae..b67504261672 100644 --- a/src/shims/fs.rs +++ b/src/shims/fs.rs @@ -292,7 +292,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx if src.is_dir() { fs::symlink_dir(src, dst) } else { - fs::symlink(src, dst) + fs::symlink_file(src, dst) } }