Skip to content

do not run symlink tests on Windows hosts #3591

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions tests/pass-dep/libc/libc-fs-readlink.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Symlink tests are separate since they don't in general work on a Windows host.
//@ignore-host-windows: creating symlinks requires admin permissions on Windows
//@ignore-target-windows: File handling is not implemented yet
//@compile-flags: -Zmiri-disable-isolation

use std::ffi::CString;
use std::io::{Error, ErrorKind};
use std::os::unix::ffi::OsStrExt;

#[path = "../../utils/mod.rs"]
mod utils;

fn main() {
let bytes = b"Hello, World!\n";
let path = utils::prepare_with_content("miri_test_fs_link_target.txt", bytes);
let expected_path = path.as_os_str().as_bytes();

let symlink_path = utils::prepare("miri_test_fs_symlink.txt");
std::os::unix::fs::symlink(&path, &symlink_path).unwrap();

// 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()) };
// Check that the resolved path was properly written into the buf.
assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path);
assert_eq!(large_buf.last(), Some(&0xFF));
assert_eq!(res, large_buf.len() as isize - 1);

// 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!(small_buf, &expected_path[..small_buf.len()]);
assert_eq!(res, small_buf.len() as isize);

// 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);
}
2 changes: 1 addition & 1 deletion tests/pass-dep/libc/libc-fs-with-isolation.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//@ignore-target-windows: no libc on Windows
//@ignore-target-windows: File handling is not implemented yet
//@compile-flags: -Zmiri-isolation-error=warn-nobacktrace
//@normalize-stderr-test: "(stat(x)?)" -> "$$STAT"

Expand Down
84 changes: 9 additions & 75 deletions tests/pass-dep/libc/libc-fs.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//@ignore-target-windows: no libc on Windows
//@ignore-target-windows: File handling is not implemented yet
//@compile-flags: -Zmiri-disable-isolation

#![feature(io_error_more)]
#![feature(io_error_uncategorized)]

use std::ffi::{CStr, CString, OsString};
use std::fs::{canonicalize, remove_dir_all, remove_file, File};
use std::fs::{canonicalize, remove_file, File};
use std::io::{Error, ErrorKind, Write};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::AsRawFd;
Expand All @@ -21,7 +21,6 @@ fn main() {
test_ftruncate::<libc::off_t>(libc::ftruncate);
#[cfg(target_os = "linux")]
test_ftruncate::<libc::off64_t>(libc::ftruncate64);
test_readlink();
test_file_open_unix_allow_two_args();
test_file_open_unix_needs_three_args();
test_file_open_unix_extra_third_arg();
Expand All @@ -38,33 +37,8 @@ fn main() {
test_isatty();
}

/// Prepare: compute filename and make sure the file does not exist.
fn prepare(filename: &str) -> PathBuf {
let path = utils::tmp().join(filename);
// Clean the paths for robustness.
remove_file(&path).ok();
path
}

/// Prepare directory: compute directory name and make sure it does not exist.
#[allow(unused)]
fn prepare_dir(dirname: &str) -> PathBuf {
let path = utils::tmp().join(&dirname);
// Clean the directory for robustness.
remove_dir_all(&path).ok();
path
}

/// Prepare like above, and also write some initial content to the file.
fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf {
let path = prepare(filename);
let mut file = File::create(&path).unwrap();
file.write(content).unwrap();
path
}

fn test_file_open_unix_allow_two_args() {
let path = prepare_with_content("test_file_open_unix_allow_two_args.txt", &[]);
let path = utils::prepare_with_content("test_file_open_unix_allow_two_args.txt", &[]);

let mut name = path.into_os_string();
name.push("\0");
Expand All @@ -73,7 +47,7 @@ fn test_file_open_unix_allow_two_args() {
}

fn test_file_open_unix_needs_three_args() {
let path = prepare_with_content("test_file_open_unix_needs_three_args.txt", &[]);
let path = utils::prepare_with_content("test_file_open_unix_needs_three_args.txt", &[]);

let mut name = path.into_os_string();
name.push("\0");
Expand All @@ -82,7 +56,7 @@ fn test_file_open_unix_needs_three_args() {
}

fn test_file_open_unix_extra_third_arg() {
let path = prepare_with_content("test_file_open_unix_extra_third_arg.txt", &[]);
let path = utils::prepare_with_content("test_file_open_unix_extra_third_arg.txt", &[]);

let mut name = path.into_os_string();
name.push("\0");
Expand All @@ -106,49 +80,9 @@ fn test_canonicalize_too_long() {
assert!(canonicalize(too_long).is_err());
}

fn test_readlink() {
let bytes = b"Hello, World!\n";
let path = prepare_with_content("miri_test_fs_link_target.txt", bytes);
let expected_path = path.as_os_str().as_bytes();

let symlink_path = prepare("miri_test_fs_symlink.txt");
std::os::unix::fs::symlink(&path, &symlink_path).unwrap();

// 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()) };
// Check that the resolved path was properly written into the buf.
assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path);
assert_eq!(large_buf.last(), Some(&0xFF));
assert_eq!(res, large_buf.len() as isize - 1);

// 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!(small_buf, &expected_path[..small_buf.len()]);
assert_eq!(res, small_buf.len() as isize);

// 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);
}

fn test_rename() {
let path1 = prepare("miri_test_libc_fs_source.txt");
let path2 = prepare("miri_test_libc_fs_rename_destination.txt");
let path1 = utils::prepare("miri_test_libc_fs_source.txt");
let path2 = utils::prepare("miri_test_libc_fs_rename_destination.txt");

let file = File::create(&path1).unwrap();
drop(file);
Expand Down Expand Up @@ -178,7 +112,7 @@ fn test_ftruncate<T: From<i32>>(
// https://docs.rs/libc/latest/i686-unknown-linux-gnu/libc/type.off_t.html

let bytes = b"hello";
let path = prepare("miri_test_libc_fs_ftruncate.txt");
let path = utils::prepare("miri_test_libc_fs_ftruncate.txt");
let mut file = File::create(&path).unwrap();
file.write(bytes).unwrap();
file.sync_all().unwrap();
Expand Down Expand Up @@ -209,7 +143,7 @@ fn test_ftruncate<T: From<i32>>(
fn test_o_tmpfile_flag() {
use std::fs::{create_dir, OpenOptions};
use std::os::unix::fs::OpenOptionsExt;
let dir_path = prepare_dir("miri_test_fs_dir");
let dir_path = utils::prepare_dir("miri_test_fs_dir");
create_dir(&dir_path).unwrap();
// test that the `O_TMPFILE` custom flag gracefully errors instead of stopping execution
assert_eq!(
Expand Down
2 changes: 1 addition & 1 deletion tests/pass-dep/libc/libc-time.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//@ignore-target-windows: no libc on Windows
//@ignore-target-windows: no libc time APIs on Windows
//@compile-flags: -Zmiri-disable-isolation
use std::ffi::CStr;
use std::{env, mem, ptr};
Expand Down
50 changes: 50 additions & 0 deletions tests/pass/shims/fs-symlink.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Symlink tests are separate since they don't in general work on a Windows host.
//@ignore-host-windows: creating symlinks requires admin permissions on Windows
//@ignore-target-windows: File handling is not implemented yet
//@compile-flags: -Zmiri-disable-isolation

use std::fs::{read_link, remove_file, File};
use std::io::{Read, Result};
use std::path::Path;

#[path = "../../utils/mod.rs"]
mod utils;

fn check_metadata(bytes: &[u8], path: &Path) -> Result<()> {
// Test that the file metadata is correct.
let metadata = path.metadata()?;
// `path` should point to a file.
assert!(metadata.is_file());
// The size of the file must be equal to the number of written bytes.
assert_eq!(bytes.len() as u64, metadata.len());
Ok(())
}

fn main() {
let bytes = b"Hello, World!\n";
let path = utils::prepare_with_content("miri_test_fs_link_target.txt", bytes);
let symlink_path = utils::prepare("miri_test_fs_symlink.txt");

// Creating a symbolic link should succeed.
#[cfg(unix)]
std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
#[cfg(windows)]
std::os::windows::fs::symlink_file(&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 (i.e., the file it points to) is correct.
check_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());
// Check that we can follow the link.
assert_eq!(read_link(&symlink_path).unwrap(), path);
// Removing symbolic link should succeed.
remove_file(&symlink_path).unwrap();

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