From 04a69e4cf49d95a6c67af505673f1560ebad21d6 Mon Sep 17 00:00:00 2001 From: tiif Date: Sat, 23 Mar 2024 00:22:57 +0800 Subject: [PATCH] Add libc test for rename and ftruncate --- src/tools/miri/CONTRIBUTING.md | 13 ++-- .../miri/tests/pass-dep/shims/libc-fs.rs | 63 +++++++++++++++++++ 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index f2f3a642e0a..f779c09b764 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -64,19 +64,22 @@ For example, you can (cross-)run the driver on a particular file by doing ./miri run tests/pass/hello.rs --target i686-unknown-linux-gnu ``` -and you can (cross-)run the entire test suite using: +Tests in ``pass-dep`` need to be run using ``./miri run --dep ``. +For example: +```sh +./miri run --dep tests/pass-dep/shims/libc-fs.rs +``` + +You can (cross-)run the entire test suite using: ``` ./miri test MIRI_TEST_TARGET=i686-unknown-linux-gnu ./miri test ``` -If your target doesn't support libstd that should usually just work. However, if you are using a -custom target file, you might have to set `MIRI_NO_STD=1`. - `./miri test FILTER` only runs those tests that contain `FILTER` in their filename (including the base directory, e.g. `./miri test fail` will run all compile-fail tests). These filters are passed -to `cargo test`, so for multiple filers you need to use `./miri test -- FILTER1 FILTER2`. +to `cargo test`, so for multiple filters you need to use `./miri test -- FILTER1 FILTER2`. #### Fine grained logging diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs.rs b/src/tools/miri/tests/pass-dep/shims/libc-fs.rs index fafeb9e0558..4cfc1843a7a 100644 --- a/src/tools/miri/tests/pass-dep/shims/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/shims/libc-fs.rs @@ -16,6 +16,10 @@ mod utils; fn main() { test_dup_stdout_stderr(); test_canonicalize_too_long(); + test_rename(); + 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(); @@ -133,6 +137,65 @@ fn test_readlink() { 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 file = File::create(&path1).unwrap(); + drop(file); + + let c_path1 = CString::new(path1.as_os_str().as_bytes()).expect("CString::new failed"); + let c_path2 = CString::new(path2.as_os_str().as_bytes()).expect("CString::new failed"); + + // Renaming should succeed + unsafe { libc::rename(c_path1.as_ptr(), c_path2.as_ptr()) }; + // Check that old file path isn't present + assert_eq!(ErrorKind::NotFound, path1.metadata().unwrap_err().kind()); + // Check that the file has moved successfully + assert!(path2.metadata().unwrap().is_file()); + + // Renaming a nonexistent file should fail + let res = unsafe { libc::rename(c_path1.as_ptr(), c_path2.as_ptr()) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound); + + remove_file(&path2).unwrap(); +} + +fn test_ftruncate>( + ftruncate: unsafe extern "C" fn(fd: libc::c_int, length: T) -> libc::c_int, +) { + // libc::off_t is i32 in target i686-unknown-linux-gnu + // 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 mut file = File::create(&path).unwrap(); + file.write(bytes).unwrap(); + file.sync_all().unwrap(); + assert_eq!(file.metadata().unwrap().len(), 5); + + let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + let fd = unsafe { libc::open(c_path.as_ptr(), libc::O_RDWR) }; + + // Truncate to a bigger size + let mut res = unsafe { ftruncate(fd, T::from(10)) }; + assert_eq!(res, 0); + assert_eq!(file.metadata().unwrap().len(), 10); + + // Write after truncate + file.write(b"dup").unwrap(); + file.sync_all().unwrap(); + assert_eq!(file.metadata().unwrap().len(), 10); + + // Truncate to smaller size + res = unsafe { ftruncate(fd, T::from(2)) }; + assert_eq!(res, 0); + assert_eq!(file.metadata().unwrap().len(), 2); + + remove_file(&path).unwrap(); +} + #[cfg(target_os = "linux")] fn test_o_tmpfile_flag() { use std::fs::{create_dir, OpenOptions};