diff --git a/Cargo.lock b/Cargo.lock index ec220498ca..f35a6911c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -965,6 +965,13 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs" +version = "0.0.0" +dependencies = [ + "hermit", +] + [[package]] name = "fuse_test" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 7512bac1ac..e93f24b595 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "benches/multithreaded", "examples/axum", "examples/demo", + "examples/fs", "examples/fuse_test", "examples/hello_world", "examples/httpd", diff --git a/examples/fs/Cargo.toml b/examples/fs/Cargo.toml new file mode 100644 index 0000000000..aab790ef1a --- /dev/null +++ b/examples/fs/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "fs" +edition = "2024" + +[target.'cfg(target_os = "hermit")'.dependencies] +hermit = { path = "../../hermit" } diff --git a/examples/fs/src/display.rs b/examples/fs/src/display.rs new file mode 100644 index 0000000000..f3163ab1f7 --- /dev/null +++ b/examples/fs/src/display.rs @@ -0,0 +1,80 @@ +use std::fmt; +use std::fs::{FileType, Metadata}; +#[cfg(target_os = "hermit")] +use std::os::hermit::fs::MetadataExt; +#[cfg(unix)] +use std::os::unix::fs::MetadataExt; + +use libc::mode_t; + +pub struct MetadataDisplay<'a>(&'a Metadata); + +impl fmt::Display for MetadataDisplay<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let file_type = self.0.file_type().display(); + let mode = self.0.mode(); + write!(f, "{file_type}") + } +} + +pub trait MetadataExt { + fn display(&self) -> MetadataDisplay<'_>; +} + +impl MetadataExt for Metadata { + fn display(&self) -> MetadataDisplay<'_> { + MetadataDisplay(self) + } +} + +#[derive(Clone, Copy, Debug)] +enum FileTypeDisplay { + Dir, + File, + Symlink, + Unknown, +} + +impl FileTypeDisplay { + pub fn as_str(self) -> &'static str { + match self { + Self::Dir => "dir", + Self::File => "file", + Self::Symlink => "symlink", + Self::Unknown => "unknown", + } + } +} + +impl fmt::Display for FileTypeDisplay { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +trait FileTypeExt { + fn display(&self) -> FileTypeDisplay; +} + +impl FileTypeExt for FileType { + fn display(&self) -> FileTypeDisplay { + match self { + this if this.is_dir() => FileTypeDisplay::Dir, + this if this.is_file() => FileTypeDisplay::File, + this if this.is_symlink() => FileTypeDisplay::Symlink, + _ => FileTypeDisplay::Unknown, + } + } +} + +struct ModeDisplay(mode_t); + +trait ModeExt { + fn display_mode(self) -> ModeDisplay; +} + +impl ModeExt for mode_t { + fn display_mode(self) -> ModeDisplay { + ModeDisplay(self) + } +} diff --git a/examples/fs/src/main.rs b/examples/fs/src/main.rs new file mode 100644 index 0000000000..31cd9e30ea --- /dev/null +++ b/examples/fs/src/main.rs @@ -0,0 +1,161 @@ +use std::path::Path; +use std::{env, fs, io}; + +#[cfg(target_os = "hermit")] +use hermit as _; + +fn main() -> io::Result<()> { + read("/proc/version")?; + read_dir("/proc")?; + + test_dir("/tmp")?; + + + + if cfg!(target_os = "hermit") { + test_dir("/root")?; + } + + // assert_chdir(".", "/")?; + // assert_chdir("/", "/")?; + // assert_chdir("/tmp", "/tmp")?; + + Ok(()) +} + +fn assert_chdir, Q: AsRef>(path: P, expected: Q) -> io::Result<()> { + let path = path.as_ref(); + let expected = expected.as_ref(); + + eprintln!("chdir({})", path.display()); + env::set_current_dir(path)?; + + eprint!("cwd = "); + let cwd = env::current_dir()?; + eprintln!("{}", cwd.display()); + assert_eq!(cwd, expected); + + Ok(()) +} + +fn test_dir>(path: P) -> io::Result<()> { + let path = path.as_ref(); + + if let Err(err) = remove_files(path) { + eprintln!("err = {err}"); + } + + create_files(path)?; + read_dir(path)?; + remove_files(path)?; + + Ok(()) +} + +fn read>(path: P) -> io::Result<()> { + let path = path.as_ref(); + + eprint!("{} contains", path.display()); + let content = fs::read_to_string(path).unwrap(); + eprintln!(" {content:?}"); + + eprintln!(); + Ok(()) +} + +fn create_files>(dir: P) -> io::Result<()> { + let dir = dir.as_ref(); + let hello_path = dir.join("hello.txt"); + let hello_content = "Hello, world!"; + + if hello_path.exists() { + return Err(io::Error::from(io::ErrorKind::AlreadyExists)); + } + + eprint!("{:15} : writing", hello_path.display()); + fs::write(&hello_path, hello_content)?; + + eprint!(", writing"); + fs::write(&hello_path, hello_content)?; + + eprintln!(", reading"); + let read = fs::read_to_string(&hello_path)?; + assert_eq!(hello_content, read); + + eprintln!(); + Ok(()) +} + +fn remove_files>(dir: P) -> io::Result<()> { + let dir = dir.as_ref(); + let hello_path = dir.join("hello.txt"); + + if !hello_path.exists() { + return Err(io::Error::from(io::ErrorKind::NotFound)); + } + + eprintln!("{:15} : removing", hello_path.display()); + fs::remove_file(hello_path)?; + + eprintln!(); + Ok(()) +} + +fn read_dir>(path: P) -> io::Result<()> { + let path = path.as_ref(); + eprintln!(); + + assert!(path.is_dir()); + eprintln!("Reading {path:?} directory entries"); + let entries = fs::read_dir(path)?.collect::, _>>()?; + + assert!(!entries.is_empty()); + for entry in entries { + let path = entry.path(); + eprintln!("Found {path:?}"); + + let lstat = entry.metadata()?; + eprintln!("lstat = {lstat:?}"); + + // FIXME: + // assert_eq!(entry.file_type()?, lstat.file_type()); + + // FIXME: + // let path_link = path.read_link()?; + // eprintln!("path_link = {}", path_link.display()); + + let stat = path.metadata()?; + eprintln!("stat = {stat:?}"); + + eprintln!(); + } + + eprintln!(); + Ok(()) +} + +mod fuse_test { + use std::fs; + use std::path::Path; + + #[cfg(target_os = "hermit")] + const TEST_DIR: &str = "/root"; + #[cfg(not(target_os = "hermit"))] + const TEST_DIR: &str = "/tmp/data"; + + pub fn main() { + let test_path = Path::new(TEST_DIR); + assert!(test_path.is_dir()); + let paths = fs::read_dir(test_path).unwrap(); + + let path_to_new_dir = test_path.join("new_dir"); + + assert!(!path_to_new_dir.exists()); + fs::create_dir(&path_to_new_dir).unwrap(); + assert!(path_to_new_dir.exists()); + + assert!(path_to_new_dir.exists()); + fs::remove_dir(&path_to_new_dir).unwrap(); + assert!(!path_to_new_dir.exists()); + } +}