diff --git a/tests/pass-dep/libc/libc-fs-symlink.rs b/tests/pass-dep/libc/libc-fs-symlink.rs new file mode 100644 index 0000000000..1c875ff7d4 --- /dev/null +++ b/tests/pass-dep/libc/libc-fs-symlink.rs @@ -0,0 +1,68 @@ +// 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::fs; +use std::io::{Error, ErrorKind}; +use std::os::unix::ffi::OsStrExt; +use std::path::PathBuf; + +#[path = "../../utils/mod.rs"] +mod utils; + +/// 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. + fs::remove_file(&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); + fs::write(&path, content).unwrap(); + path +} + +fn main() { + 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); +} diff --git a/tests/pass-dep/libc/libc-fs-with-isolation.rs b/tests/pass-dep/libc/libc-fs-with-isolation.rs index 5185db0b0e..088a632427 100644 --- a/tests/pass-dep/libc/libc-fs-with-isolation.rs +++ b/tests/pass-dep/libc/libc-fs-with-isolation.rs @@ -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" diff --git a/tests/pass-dep/libc/libc-fs.rs b/tests/pass-dep/libc/libc-fs.rs index 0dd849a045..7d0cdfa466 100644 --- a/tests/pass-dep/libc/libc-fs.rs +++ b/tests/pass-dep/libc/libc-fs.rs @@ -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::{self, canonicalize, remove_file, File}; use std::io::{Error, ErrorKind, Write}; use std::os::unix::ffi::OsStrExt; use std::os::unix::io::AsRawFd; @@ -21,7 +21,6 @@ fn main() { test_ftruncate::(libc::ftruncate); #[cfg(target_os = "linux")] test_ftruncate::(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(); @@ -42,24 +41,22 @@ fn main() { fn prepare(filename: &str) -> PathBuf { let path = utils::tmp().join(filename); // Clean the paths for robustness. - remove_file(&path).ok(); + fs::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(); + fs::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(); + fs::write(&path, content).unwrap(); path } @@ -106,46 +103,6 @@ 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"); diff --git a/tests/pass-dep/libc/libc-time.rs b/tests/pass-dep/libc/libc-time.rs index 69c75bd8ca..ea1e49a072 100644 --- a/tests/pass-dep/libc/libc-time.rs +++ b/tests/pass-dep/libc/libc-time.rs @@ -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}; diff --git a/tests/pass/shims/fs-symlink.rs b/tests/pass/shims/fs-symlink.rs new file mode 100644 index 0000000000..835e801824 --- /dev/null +++ b/tests/pass/shims/fs-symlink.rs @@ -0,0 +1,65 @@ +// 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::{self, read_link, remove_file, File}; +use std::io::{Read, Result}; +use std::path::{Path, PathBuf}; + +#[path = "../../utils/mod.rs"] +mod utils; + +/// 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. + fs::remove_file(&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); + fs::write(&path, content).unwrap(); + path +} + +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 = prepare_with_content("miri_test_fs_link_target.txt", bytes); + let symlink_path = 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(); +} diff --git a/tests/pass/shims/fs.rs b/tests/pass/shims/fs.rs index 8a500b857b..c34e35c9bc 100644 --- a/tests/pass/shims/fs.rs +++ b/tests/pass/shims/fs.rs @@ -1,17 +1,13 @@ //@ignore-target-windows: File handling is not implemented yet //@compile-flags: -Zmiri-disable-isolation -// If this test is failing for you locally, you can try -// 1. Deleting the files `/tmp/miri_*` -// 2. Setting `MIRI_TEMP` or `TMPDIR` to a different directory, without the `miri_*` files - #![feature(io_error_more)] #![feature(io_error_uncategorized)] use std::collections::HashMap; use std::ffi::OsString; use std::fs::{ - canonicalize, create_dir, read_dir, read_link, remove_dir, remove_dir_all, remove_file, rename, + self, canonicalize, create_dir, read_dir, remove_dir, remove_dir_all, remove_file, rename, File, OpenOptions, }; use std::io::{Error, ErrorKind, IsTerminal, Read, Result, Seek, SeekFrom, Write}; @@ -29,7 +25,6 @@ fn main() { test_metadata(); test_file_set_len(); test_file_sync(); - test_symlink(); test_errors(); test_rename(); test_directory(); @@ -41,7 +36,7 @@ fn main() { fn prepare(filename: &str) -> PathBuf { let path = utils::tmp().join(filename); // Clean the paths for robustness. - remove_file(&path).ok(); + fs::remove_file(&path).ok(); path } @@ -49,15 +44,14 @@ fn prepare(filename: &str) -> PathBuf { fn prepare_dir(dirname: &str) -> PathBuf { let path = utils::tmp().join(&dirname); // Clean the directory for robustness. - remove_dir_all(&path).ok(); + fs::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(); + fs::write(&path, content).unwrap(); path } @@ -223,35 +217,6 @@ fn test_file_sync() { remove_file(&path).unwrap(); } -fn test_symlink() { - let bytes = b"Hello, World!\n"; - let path = prepare_with_content("miri_test_fs_link_target.txt", bytes); - let symlink_path = 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(); -} - fn test_errors() { let bytes = b"Hello, World!\n"; let path = prepare("miri_test_fs_errors.txt");