diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 4a1d62d3567..ff5ffb0e3ac 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -521,6 +521,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } + "rename" => { + let result = this.rename(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 f0efc073f75..2ae215e7204 100644 --- a/src/shims/fs.rs +++ b/src/shims/fs.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; -use std::fs::{remove_file, File, OpenOptions}; +use std::fs::{remove_file, rename, File, OpenOptions}; use std::io::{Read, Seek, SeekFrom, Write}; use std::path::PathBuf; use std::time::SystemTime; @@ -582,6 +582,32 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.set_last_error(ebadf)?; Ok((-1).into()) } + + fn rename( + &mut self, + oldpath_op: OpTy<'tcx, Tag>, + newpath_op: OpTy<'tcx, Tag>, + ) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + this.check_no_isolation("rename")?; + + let oldpath_scalar = this.read_scalar(oldpath_op)?.not_undef()?; + let newpath_scalar = this.read_scalar(newpath_op)?.not_undef()?; + + if this.is_null(oldpath_scalar)? || this.is_null(newpath_scalar)? { + let efault = this.eval_libc("EFAULT")?; + this.set_last_error(efault)?; + return Ok(-1); + } + + let oldpath = this.read_os_str_from_c_str(oldpath_scalar)?; + let newpath = this.read_os_str_from_c_str(newpath_scalar)?; + + let result = rename(oldpath, newpath).map(|_| 0); + + this.try_unwrap_io_result(result) + } } /// Extracts the number of seconds and nanoseconds elapsed between `time` and the unix epoch when diff --git a/tests/run-pass/fs.rs b/tests/run-pass/fs.rs index 7483bf3ec8b..71c6e854f7c 100644 --- a/tests/run-pass/fs.rs +++ b/tests/run-pass/fs.rs @@ -1,7 +1,7 @@ // ignore-windows: File handling is not implemented yet // compile-flags: -Zmiri-disable-isolation -use std::fs::{File, remove_file}; +use std::fs::{File, remove_file, rename}; use std::io::{Read, Write, ErrorKind, Result, Seek, SeekFrom}; use std::path::{PathBuf, Path}; @@ -82,6 +82,19 @@ fn main() { // Removing file should succeed. remove_file(&path).unwrap(); + // Renaming a file should succeed. + let path1 = tmp.join("rename_source.txt"); + let path2 = tmp.join("rename_destination.txt"); + // Clean files for robustness. + remove_file(&path1).ok(); + remove_file(&path2).ok(); + let file = File::create(&path1).unwrap(); + drop(file); + rename(&path1, &path2).unwrap(); + assert_eq!(ErrorKind::NotFound, path1.metadata().unwrap_err().kind()); + assert!(path2.metadata().unwrap().is_file()); + remove_file(&path2).unwrap(); + // The two following tests also check that the `__errno_location()` shim is working properly. // Opening a non-existing file should fail with a "not found" error. assert_eq!(ErrorKind::NotFound, File::open(&path).unwrap_err().kind());