scalar: Scalar<Tag>,
size: u64,
) -> InterpResult<'tcx, (bool, u64)> {
- #[cfg(unix)]
- fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
- Ok(os_str.as_bytes())
- }
- #[cfg(not(unix))]
- fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
- // On non-unix platforms the best we can do to transform bytes from/to OS strings is to do the
- // intermediate transformation into strings. Which invalidates non-utf8 paths that are actually
- // valid.
- os_str
- .to_str()
- .map(|s| s.as_bytes())
- .ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str).into())
- }
- let bytes = os_str_to_bytes(os_str)?;
+ let bytes = self.os_str_to_bytes(os_str)?;
// If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required null
// terminator to memory using the `ptr` pointer would cause an out-of-bounds access.
let string_length = u64::try_from(bytes.len()).unwrap();
let os_str = convert_path_separator(Cow::Borrowed(path.as_os_str()), &this.tcx.sess.target.target.target_os, Pathconversion::HostToTarget);
this.write_os_str_to_wide_str(&os_str, scalar, size)
}
+
+ #[cfg(unix)]
+ fn os_str_to_bytes<'a>(&mut self, os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
+ Ok(os_str.as_bytes())
+ }
+
+ #[cfg(not(unix))]
+ fn os_str_to_bytes<'a>(&mut self, os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
+ // On non-unix platforms the best we can do to transform bytes from/to OS strings is to do the
+ // intermediate transformation into strings. Which invalidates non-utf8 paths that are actually
+ // valid.
+ os_str
+ .to_str()
+ .map(|s| s.as_bytes())
+ .ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str).into())
+ }
+
}
let result = this.fdatasync(fd)?;
this.write_scalar(Scalar::from_i32(result), dest)?;
}
+ "readlink" => {
+ let &[pathname, buf, bufsize] = check_arg_count(args)?;
+ let result = this.readlink(pathname, buf, bufsize)?;
+ this.write_scalar(Scalar::from_machine_isize(result, this), dest)?;
+ }
// Allocation
"posix_memalign" => {
this.handle_not_found()
}
}
+
+ fn readlink(
+ &mut self,
+ pathname_op: OpTy<'tcx, Tag>,
+ buf_op: OpTy<'tcx, Tag>,
+ bufsize_op: OpTy<'tcx, Tag>
+ ) -> InterpResult<'tcx, i64> {
+ let this = self.eval_context_mut();
+
+ this.check_no_isolation("readlink")?;
+
+ let pathname = this.read_path_from_c_str(this.read_scalar(pathname_op)?.check_init()?)?;
+ let buf = this.read_scalar(buf_op)?.check_init()?;
+ let bufsize = this.read_scalar(bufsize_op)?.to_machine_usize(this)?;
+
+ let result = std::fs::read_link(pathname);
+ match result {
+ Ok(resolved) => {
+ let mut path_bytes = this.os_str_to_bytes(resolved.as_ref())?;
+ if path_bytes.len() > bufsize as usize {
+ path_bytes = &path_bytes[..(bufsize as usize)]
+ }
+ // 'readlink' truncates the resolved path if
+ // the provided buffer is not large enough
+ this.memory.write_bytes(buf, path_bytes.iter().copied())?;
+ Ok(path_bytes.len() as i64)
+ }
+ Err(e) => {
+ this.set_last_error_from_io_error(e)?;
+ Ok(-1)
+ }
+ }
+ }
}
/// Extracts the number of seconds and nanoseconds elapsed between `time` and the unix epoch when
// ignore-windows: File handling is not implemented yet
// compile-flags: -Zmiri-disable-isolation
+#![feature(rustc_private)]
+
use std::fs::{
File, create_dir, OpenOptions, read_dir, remove_dir, remove_dir_all, remove_file, rename,
};
-use std::io::{Read, Write, ErrorKind, Result, Seek, SeekFrom};
+use std::ffi::CString;
+use std::io::{Read, Write, Error, ErrorKind, Result, Seek, SeekFrom};
use std::path::{PathBuf, Path};
+extern crate libc;
+
+
fn main() {
test_file();
test_file_clone();
let mut contents = Vec::new();
symlink_file.read_to_end(&mut contents).unwrap();
assert_eq!(bytes, contents.as_slice());
+
+
+ #[cfg(unix)]
+ {
+ use std::os::unix::ffi::OsStrExt;
+
+ let expected_path = path.as_os_str().as_bytes();
+
+ // Test that the expected string gets written to a buffer of proper
+ // length, and that a trailing null byte is not written
+ let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap();
+ let symlink_c_ptr = symlink_c_str.as_ptr();
+
+ // Make the buf one byte larger than it needs to be,
+ // and check that the last byte is not overwritten
+ let mut large_buf = vec![0xFF; expected_path.len() + 1];
+ let res = unsafe { libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) };
+ assert_eq!(res, large_buf.len() as isize - 1);
+ // Check that the resovled path was properly written into the buf
+ assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path);
+ assert_eq!(large_buf.last(), Some(&0xFF));
+
+ // Test that the resolved path is truncated if the provided buffer
+ // is too small.
+ let mut small_buf = [0u8; 2];
+ let res = unsafe { libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) };
+ assert_eq!(res, small_buf.len() as isize);
+ assert_eq!(small_buf, &expected_path[..small_buf.len()]);
+
+ // Test that we report a proper error for a missing path.
+ let bad_path = CString::new("MIRI_MISSING_FILE_NAME").unwrap();
+ let res = unsafe { libc::readlink(bad_path.as_ptr(), small_buf.as_mut_ptr().cast(), small_buf.len()) };
+ assert_eq!(res, -1);
+ assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound);
+ }
+
+
// Test that metadata of a symbolic link is correct.
check_metadata(bytes, &symlink_path).unwrap();
// Test that the metadata of a symbolic link is correct when not following it.