From 337ba2790aadfabb6a33c76128f743476b32501f Mon Sep 17 00:00:00 2001 From: llenotre Date: Mon, 22 Jul 2024 00:13:11 +0200 Subject: [PATCH] feat: test symlinks, rename and fifo --- src/filesystem.rs | 111 +++++++++++++++++++++++++++++++++++++++++----- src/main.rs | 29 ++++++++---- src/util.rs | 10 +++++ 3 files changed, 130 insertions(+), 20 deletions(-) diff --git a/src/filesystem.rs b/src/filesystem.rs index c763273..4bfbb8d 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -1,4 +1,4 @@ -//! TODO doc +//! Filesystem testing. use crate::util::{TestError, TestResult}; use crate::{log, test_assert, test_assert_eq, util}; @@ -10,6 +10,8 @@ use std::io::Seek; use std::io::SeekFrom; use std::io::Write; use std::os::fd::AsRawFd; +use std::os::unix; +use std::os::unix::fs::MetadataExt; use std::path::Path; pub fn basic() -> TestResult { @@ -147,29 +149,114 @@ pub fn directories() -> TestResult { pub fn hardlinks() -> TestResult { log!("Create link to directory (invalid)"); - fs::create_dir("/test_dir")?; - let res = fs::hard_link("/test_dir", "/bad_link"); + fs::create_dir("test_dir")?; + let res = fs::hard_link("test_dir", "bad_link"); test_assert!(matches!(res, Err(e) if e.kind() == io::ErrorKind::PermissionDenied)); // Check the link has not been created - let res = fs::remove_dir("/bad_link"); + let res = fs::remove_dir("bad_link"); test_assert!(matches!(res, Err(e) if e.kind() == io::ErrorKind::NotFound)); log!("Cleanup"); - fs::remove_dir("/test_dir")?; + fs::remove_dir("test_dir")?; log!("Create link to file"); - fs::hard_link("/maestro-test", "/good_link")?; - let inode0 = util::stat("/maestro-test")?.st_ino; - let inode1 = util::stat("/good_link")?.st_ino; + fs::hard_link("maestro-test", "good_link")?; + let inode0 = util::stat("maestro-test")?.st_ino; + let inode1 = util::stat("good_link")?.st_ino; test_assert_eq!(inode0, inode1); log!("Remove link to file"); - fs::remove_file("/good_link")?; - util::stat("/maestro-test")?; - let res = util::stat("/good_link"); + fs::remove_file("good_link")?; + util::stat("maestro-test")?; + let res = util::stat("good_link"); test_assert!(matches!(res, Err(e) if e.kind() == io::ErrorKind::NotFound)); log!("Create link to file that don't exist (invalid)"); - let res = fs::hard_link("/not_found", "/link"); + let res = fs::hard_link("not_found", "link"); test_assert!(matches!(res, Err(e) if e.kind() == io::ErrorKind::NotFound)); Ok(()) } + +pub fn symlinks() -> TestResult { + log!("Create link"); + unix::fs::symlink("maestro-test", "testlink")?; + log!("Cleanup"); + fs::remove_file("testlink")?; + + log!("Create directory"); + fs::create_dir("target")?; + log!("Create link to directory"); + unix::fs::symlink("link", "target")?; + log!("Stat link"); + test_assert!(fs::metadata("link")?.is_symlink()); + log!("Stat directory"); + test_assert!(fs::metadata("link/")?.is_dir()); + + log!("Make dangling"); + fs::remove_dir("target")?; + log!("Stat link"); + test_assert!(fs::metadata("link")?.is_symlink()); + log!("Stat directory"); + test_assert!(matches!(fs::metadata("link/"), Err(e) if e.kind() == io::ErrorKind::NotFound)); + log!("Cleanup"); + fs::remove_file("link")?; + + Ok(()) +} + +pub fn rename() -> TestResult { + log!("Create file"); + { + let mut file = OpenOptions::new() + .create_new(true) + .read(true) + .write(true) + .open("old")?; + file.write_all(b"abcdef")?; + } + + log!("Rename"); + fs::rename("old", "new")?; + log!("Stat old file"); + test_assert!(matches!(fs::metadata("old"), Err(e) if e.kind() == io::ErrorKind::NotFound)); + log!("Stat new file"); + let metadata = fs::metadata("new")?; + test_assert!(metadata.is_file()); + test_assert_eq!(metadata.len(), 6); + test_assert_eq!(metadata.nlink(), 1); + log!("Read new file"); + test_assert_eq!(fs::read("new")?, b"abcdef"); + log!("Cleanup"); + fs::remove_file("new")?; + + log!("Create directories"); + fs::create_dir_all("old/foo/bar")?; + log!("Rename"); + fs::rename("old", "new")?; + log!("Stat old directory"); + test_assert!(matches!(fs::metadata("old"), Err(e) if e.kind() == io::ErrorKind::NotFound)); + log!("Stat new directories"); + for (path, links) in [("new", 3), ("foo", 3), ("bar", 2)] { + let metadata = fs::metadata(path)?; + test_assert!(metadata.is_dir()); + test_assert_eq!(metadata.nlink(), links); + } + log!("Cleanup"); + fs::remove_dir_all("new")?; + test_assert!(matches!(fs::metadata("new"), Err(e) if e.kind() == io::ErrorKind::NotFound)); + + // TODO test moving across mountpoints + + Ok(()) +} + +pub fn fifo() -> TestResult { + log!("Create fifo"); + util::mkfifo("fifo", 0o666)?; + // TODO test read/write (need another thread/process) + log!("Cleanup"); + fs::remove_file("fifo")?; + + // TODO test on tmpfs + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index d8a2955..b74feb5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,9 +2,8 @@ #![feature(io_error_more)] -use crate::util::{exec, TestResult}; +use crate::util::TestResult; use std::process::exit; -use std::process::Command; mod filesystem; mod procfs; @@ -47,17 +46,30 @@ const TESTS: &[TestSuite] = &[ }, Test { name: "hardlinks", - desc: "Test hardlinks creation", + desc: "Test hard links", start: filesystem::hardlinks, }, - // TODO symbolic links + Test { + name: "symlinks", + desc: "Test symbolic links", + start: filesystem::symlinks, + }, // TODO test with a lot of files // TODO test with big files // TODO try to fill the filesystem // TODO mount/umount (procfs and tmpfs. check /proc/mounts too) // TODO mount/umount another real filesystem - // TODO rename (including across different filesystems) - // TODO file fifo/socket (including in tmpfs) + Test { + name: "rename", + desc: "Test renaming files", + start: filesystem::rename, + }, + Test { + name: "fifo", + desc: "Test FIFO files", + start: filesystem::fifo, + }, + // TODO file socket (including in tmpfs) // TODO check /dev/* contents ], }, @@ -101,7 +113,8 @@ const TESTS: &[TestSuite] = &[ // TODO /proc/self/stat ], }, - TestSuite { + // TODO install required commands + /*TestSuite { name: "command", desc: "Basic commands testing", tests: &[ @@ -120,7 +133,7 @@ const TESTS: &[TestSuite] = &[ // TODO `cp` // TODO `rm` ], - }, + },*/ // TODO scripts (Shell/Perl) // TODO compilation (C/C++/Rust) // TODO network diff --git a/src/util.rs b/src/util.rs index 96de2c0..ea5c356 100644 --- a/src/util.rs +++ b/src/util.rs @@ -104,6 +104,16 @@ pub fn fstat(fd: c_int) -> io::Result { } } +pub fn mkfifo>(path: P, mode: mode_t) -> io::Result<()> { + let path = CString::new(path.as_ref().as_os_str().as_bytes())?; + let res = unsafe { libc::mkfifo(path.as_ptr(), mode) }; + if res >= 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } +} + pub fn mount( src: &CStr, target: &CStr,