From e205e317973b1cbd703814228645248724933e94 Mon Sep 17 00:00:00 2001 From: pjht Date: Wed, 4 Sep 2024 10:44:29 -0500 Subject: [PATCH] Initial commit --- .cargo/config.toml | 5 + .gitignore | 1 + Cargo.lock | 427 ++++++++++++++++++++++++++++++++++ Cargo.toml | 13 ++ rust-toolchain.toml | 2 + src/ext2.rs | 80 +++++++ src/ext2/block_group_table.rs | 83 +++++++ src/ext2/block_reader.rs | 52 +++++ src/ext2/dir.rs | 170 ++++++++++++++ src/ext2/file.rs | 128 ++++++++++ src/ext2/metadata.rs | 253 ++++++++++++++++++++ src/ext2/structs.rs | 232 ++++++++++++++++++ src/main.rs | 91 ++++++++ 13 files changed, 1537 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 rust-toolchain.toml create mode 100644 src/ext2.rs create mode 100644 src/ext2/block_group_table.rs create mode 100644 src/ext2/block_reader.rs create mode 100644 src/ext2/dir.rs create mode 100644 src/ext2/file.rs create mode 100644 src/ext2/metadata.rs create mode 100644 src/ext2/structs.rs create mode 100644 src/main.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..bec4b7a --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,5 @@ +[build] +target = "x86_64-unknown-mikros" + +[install] +root = "../os_build/sysroot" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..fb5ef91 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,427 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "binread" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16598dfc8e6578e9b597d9910ba2e73618385dc9f4b1d43dd92c349d6be6418f" +dependencies = [ + "binread_derive", + "rustversion", +] + +[[package]] +name = "binread_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d9672209df1714ee804b1f4d4f68c8eb2a90b1f7a07acf472f88ce198ef1fed" +dependencies = [ + "either", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + +[[package]] +name = "critical-section" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f64009896348fc5af4222e9cf7d7d82a95a256c634ebcf61c53e4ea461422242" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "ext2" +version = "0.1.0" +dependencies = [ + "binread", + "file_rpc", + "fs_rpc", + "itertools", + "parking_lot", + "syslog_rpc", + "vfs_rpc", +] + +[[package]] +name = "file_rpc" +version = "0.1.0" +dependencies = [ + "parking_lot", + "postcard", + "serde", +] + +[[package]] +name = "fs_rpc" +version = "0.1.0" +dependencies = [ + "parking_lot", + "postcard", + "serde", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "postcard" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "heapless", + "postcard-derive", + "serde", +] + +[[package]] +name = "postcard-derive" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0239fa9c1d225d4b7eb69925c25c5e082307a141e470573fbbe3a817ce6a7a37" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.209" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.209" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syslog_rpc" +version = "0.1.0" +dependencies = [ + "parking_lot", + "postcard", + "syslog_structs", +] + +[[package]] +name = "syslog_structs" +version = "0.1.0" +dependencies = [ + "serde", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "vfs_rpc" +version = "0.1.0" +dependencies = [ + "parking_lot", + "postcard", + "serde", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..812a7c7 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "ext2" +version = "0.1.0" +edition = "2021" + +[dependencies] +binread = "2.2.0" +file_rpc = { version = "0.1.0", path = "../file_rpc" } +fs_rpc = { version = "0.1.0", path = "../fs_rpc" } +itertools = "0.13.0" +parking_lot = "0.12.3" +syslog_rpc = { version = "0.1.0", path = "../syslog/syslog_rpc" } +vfs_rpc = { version = "0.1.0", path = "../vfs/vfs_rpc" } diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..a5535e3 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "dev-x86_64-unknown-mikros" diff --git a/src/ext2.rs b/src/ext2.rs new file mode 100644 index 0000000..15f1caa --- /dev/null +++ b/src/ext2.rs @@ -0,0 +1,80 @@ +mod block_group_table; +mod block_reader; +mod dir; +mod file; +mod metadata; +mod structs; + +use block_group_table::BlockGroupDescriptorTable; +use block_reader::BlockReader; +use parking_lot::RwLock; +use std::collections::HashMap; +use std::io; +use std::path::{Path, PathBuf}; +use structs::{DirEntryDisk, Inode, Superblock}; + +pub use dir::ReadDir; +pub use file::File; +pub use metadata::Metadata; +use std::fs::File as StdFile; + +#[derive(Debug)] +pub struct Ext2 { + reader: BlockReader, + descriptor_table: BlockGroupDescriptorTable, + inode_cache: RwLock>, + dentry_cache: RwLock>, +} + +impl Ext2 { + pub fn new(mut disk: StdFile) -> io::Result { + let superblock = Superblock::read_from_disk(&mut disk)?; + let reader = BlockReader::new(disk, &superblock); + Ok(Self { + descriptor_table: BlockGroupDescriptorTable::new(&superblock, &reader)?, + reader, + inode_cache: RwLock::new(HashMap::new()), + dentry_cache: RwLock::new(HashMap::new()), + }) + } + + fn path_inode>(&self, path: P) -> io::Result { + let path = path.as_ref(); + let dentry = { self.dentry_cache.read().get(path).cloned() }; + if let Some(dentry) = dentry { + //println!("Dentry {}: In cache", path.display()); + Ok(dentry.inode) + } else if let Some(parent) = path.parent() { + //println!("Dentry {}: Not in cache", path.display()); + let file_name = path.file_name().ok_or_else(|| { + io::Error::new( + io::ErrorKind::Other, + "A path ending in .. or . was passed to path_inode", + ) + })?; + let dir_inode = self.path_inode(parent)?; + let dir = ReadDir::read_inode(self, dir_inode, parent.to_owned())?; + let entry = dir.get_entry(file_name)?; + Ok(entry.inode) + } else { + Ok(2) + } + } + + #[allow(unused)] + pub fn read_dir>(&self, path: P) -> io::Result { + let path = path.as_ref(); + let inode = self.path_inode(path)?; + ReadDir::read_inode(self, inode, path.to_owned()) + } + + #[allow(unused)] + pub fn open>(&self, path: P) -> io::Result { + File::from_inode(Inode::read(self, self.path_inode(path)?)?, self) + } + + #[allow(unused)] + pub fn metadata>(&self, path: P) -> io::Result { + Inode::read(self, self.path_inode(path)?).map(|x| Metadata::from_inode(&x)) + } +} diff --git a/src/ext2/block_group_table.rs b/src/ext2/block_group_table.rs new file mode 100644 index 0000000..40331d4 --- /dev/null +++ b/src/ext2/block_group_table.rs @@ -0,0 +1,83 @@ +use super::{ + block_reader::BlockReader, + structs::{BlockGroupDescriptor, Inode, Superblock}, +}; +use binread::prelude::*; +use std::io; +use std::vec::Vec; + +const INODE_SIZE: usize = 256; + +#[derive(Debug, BinRead)] +#[br(import(len: usize, block_group_inode_count: u32, block_size: usize))] +pub struct BlockGroupDescriptorTable { + #[br(count = len)] + table: Vec, + #[br(calc = block_group_inode_count)] + block_group_inode_count: u32, + #[br(calc = block_size)] + block_size: usize, +} + +impl BlockGroupDescriptorTable { + pub fn new(superblock: &Superblock, reader: &BlockReader) -> io::Result { + let num_block_groups = superblock + .total_blocks + .div_ceil(superblock.block_group_block_count) as usize; + let table_blocks = num_block_groups.div_ceil(reader.block_size / 32); + Ok(reader + .read_blocks(superblock.superblock_block + 1, table_blocks)? + .read_le_args(( + num_block_groups, + superblock.block_group_inode_count, + 1024 << superblock.block_size_raw, + )) + .expect("Parsing the block group descriptor table should never fail")) + } + + fn inode_group(&self, inode: u32) -> &BlockGroupDescriptor { + &self.table[((inode - 1) / self.block_group_inode_count) as usize] + } + + fn inode_group_offset(&self, inode: u32) -> usize { + ((inode - 1) % self.block_group_inode_count) as usize + } + + fn inode_block(&self, inode: u32) -> u32 { + (((self.inode_group_offset(inode) * INODE_SIZE) / self.block_size) as u32) + + self.inode_group(inode).inode_table_start + } + + fn inode_block_byte_start(&self, inode: u32) -> usize { + (self.inode_group_offset(inode) * INODE_SIZE) % self.block_size + } + + pub fn inode_loc(&self, inode: u32) -> InodeLoc { + InodeLoc { + block: self.inode_block(inode), + byte_offset: self.inode_block_byte_start(inode), + num: inode, + } + } +} + +#[derive(Debug)] +pub struct InodeLoc { + block: u32, + byte_offset: usize, + num: u32, +} + +impl InodeLoc { + pub fn read(&self, reader: &BlockReader) -> io::Result { + //println!("InodeLoc::read"); + reader + .read_block_offset(self.block, self.byte_offset as u32)? + .read_le_args((self.num,)) + .map_err(|err| match err { + binread::Error::Io(_) => io::Error::new(io::ErrorKind::Other, "Binread IO error"), + _ => io::Error::new(io::ErrorKind::InvalidData, "Inode data was invalid"), + }) + //println!("InodeLoc::read done"); + } +} diff --git a/src/ext2/block_reader.rs b/src/ext2/block_reader.rs new file mode 100644 index 0000000..18c29da --- /dev/null +++ b/src/ext2/block_reader.rs @@ -0,0 +1,52 @@ +use std::vec::Vec; + +use super::structs::Superblock; +use std::fs::File as StdFile; +use std::io::{self, Cursor, Read, Seek, SeekFrom}; + +#[derive(Debug)] +pub struct BlockReader { + pub disk: StdFile, + pub block_size: usize, +} + +impl BlockReader { + pub fn new(disk: StdFile, superblock: &Superblock) -> Self { + Self { + disk, + block_size: 1024 << superblock.block_size_raw, + } + } + + pub fn read_block(&self, block: u32) -> io::Result>> { + //println!("Read block {}", block); + let mut disk = &self.disk; + let start = self.block_to_byte_offset(block); + let size = self.block_size; + let mut vec = vec![0; size]; + disk.seek(SeekFrom::Start(start))?; + disk.read_exact(&mut vec)?; + Ok(Cursor::new(vec)) + } + + pub fn read_block_offset(&self, block: u32, offset: u32) -> io::Result>> { + let block = self.read_block(block)?.into_inner(); + let trunc_block = block[(offset as usize)..].to_vec(); + Ok(Cursor::new(trunc_block)) + } + + pub fn read_blocks(&self, block: u32, count: usize) -> io::Result>> { + let mut vec = vec![0; self.block_size * count]; + for block_no in block..(block + count as u32) { + let data = self.read_block(block)?.into_inner(); + vec[((block_no - block) as usize * self.block_size) + ..(((block_no - block) as usize + 1) * self.block_size)] + .copy_from_slice(&data); + } + Ok(Cursor::new(vec)) + } + + fn block_to_byte_offset(&self, block: u32) -> u64 { + u64::from(block) * (self.block_size as u64) + } +} diff --git a/src/ext2/dir.rs b/src/ext2/dir.rs new file mode 100644 index 0000000..9498d7c --- /dev/null +++ b/src/ext2/dir.rs @@ -0,0 +1,170 @@ +use super::{ + file::{File, FileReader}, + metadata::{FileType, Metadata}, + structs::{DirEntryDisk, Inode}, + Ext2, +}; +use std::string::String; +use std::{ffi::OsStr, io, os::mikros::ffi::OsStrExt}; +use std::{io::BufReader, path::PathBuf}; + +/// Entries returned by the [`ReadDir`] iterator. +/// +/// An instance of `DirEntry` represents an entry inside of a directory on the +/// filesystem. Each entry can be inspected via methods to learn about the full +/// path or possibly other metadata through per-platform extension traits. +#[allow(unused)] +#[allow(clippy::module_name_repetitions)] +pub struct DirEntry<'a> { + pub(super) inode: u32, + pub(super) name: String, + pub(super) metadata: Metadata, + pub(super) directory_path: PathBuf, + fs: &'a Ext2, +} + +impl<'a> core::fmt::Debug for DirEntry<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("DirEntry") + .field("inode", &self.inode) + .field("name", &self.name) + .field("metadata", &self.metadata) + .field("directory_path", &self.directory_path) + .finish() + } +} + +#[allow(unused)] +impl<'a> DirEntry<'a> { + /// Returns the bare file name of this directory entry without any other + /// leading path component. + pub fn file_name(&self) -> String { + self.name.clone() + } + + /// Returns the file type for the file that this entry points at. + /// + /// This function will not traverse symlinks if this entry points at a + /// symlink. + pub fn file_type(&self) -> FileType { + self.metadata.file_type() + } + + /// Returns the metadata for the file that this entry points at. + /// + /// This function will not traverse symlinks if this entry points at a + /// symlink. + pub fn metadata(&self) -> Metadata { + self.metadata + } + + /// Returns the full path to the file that this entry represents. + /// + /// The full path is created by joining the original path to `read_dir` + /// with the filename of this entry. + /// + /// # Panics + /// + /// This function will panic if this entry came either directly from a `ReadDir` made with + /// `Ext2::read_dir_no_path`, or indirectly from such a `ReadDir` by use of the + /// `DirEntry::read_dir` function. This is an explicit decision, so such a panic indicates a + /// definite bug in your code. + pub fn path(&self) -> PathBuf { + self.directory_path.join(self.name.clone()) + } + + pub fn open(&self) -> io::Result { + File::from_inode(Inode::read(self.fs, self.inode)?, self.fs) + } + + pub fn read_dir(&self) -> io::Result> { + ReadDir::read_inode(self.fs, self.inode, self.path()) + } +} + +/// Iterator over the entries in a directory. +/// +/// This iterator is returned from the [`super::Ext2::read_dir`] function and +/// will yield instances of [`io::Result`]<[`DirEntry`]>. Through a [`DirEntry`] +/// information like the entry's path and possibly other metadata can be +/// learned. +/// +/// The order in which this iterator returns entries is platform and filesystem +/// dependent. +/// +/// # Errors +/// +/// This [`io::Result`] will be an `Err` if there's some sort of intermittent +/// IO error during iteration. +#[derive(Debug)] +#[allow(clippy::module_name_repetitions)] +pub struct ReadDir<'a> { + fs: &'a Ext2, + file: BufReader>, + directory_path: PathBuf, +} + +impl<'a> ReadDir<'a> { + pub fn read_inode(fs: &'a Ext2, inode: u32, path: PathBuf) -> io::Result> { + Ok(Self { + fs, + file: BufReader::with_capacity( + 1024, + FileReader::new(File::from_inode_dir(Inode::read(fs, inode)?, fs)?, fs), + ), + directory_path: path, + }) + } + + pub(super) fn get_entry(mut self, name: &OsStr) -> io::Result> { + self.find(|entry| { + if let Ok(entry) = entry { + entry.name.as_bytes() == name.as_bytes() + } else { + true + } + }) + .unwrap_or_else(|| Err(io::Error::new(io::ErrorKind::NotFound, "File not found"))) + } + + fn next_impl(&mut self) -> io::Result>> { + let entry: DirEntryDisk = match DirEntryDisk::read(&mut self.file) { + Ok(entry) => entry, + Err(error) => { + return if error.kind() == io::ErrorKind::UnexpectedEof { + Ok(None) + } else { + Err(error) + } + } + }; + let name = String::from_utf8(entry.name.clone()).map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidData, + "Non UTF-8 directory entry names are unspported", + ) + })?; + if name == "." || name == ".." || entry.inode == 0 { + return self.next_impl(); + } + let entry_inode = Inode::read(self.fs, entry.inode)?; + self.fs + .dentry_cache + .write() + .insert(self.directory_path.join(name.clone()), entry.clone()); + Ok(Some(DirEntry { + inode: entry.inode, + name, + metadata: Metadata::from_inode(&entry_inode), + directory_path: self.directory_path.clone(), + fs: self.fs, + })) + } +} + +impl<'a> Iterator for ReadDir<'a> { + type Item = io::Result>; + fn next(&mut self) -> Option { + self.next_impl().transpose() + } +} diff --git a/src/ext2/file.rs b/src/ext2/file.rs new file mode 100644 index 0000000..c1cc15e --- /dev/null +++ b/src/ext2/file.rs @@ -0,0 +1,128 @@ +use super::Ext2; +use super::{structs::Inode, Metadata}; +use std::cmp::Ordering; +use std::io::{self, Read}; + +/// A reference to an open file on the filesystem. +/// +/// An instance of a `File` can be read and/or written depending on what options +/// it was opened with. Files also implement [`Seek`] to alter the logical cursor +/// that the file contains internally. +/// +/// Files are automatically closed when they go out of scope. Errors detected +/// on closing are ignored by the implementation of `Drop`. +#[derive(Debug)] +pub struct File { + inode: Inode, + block_size: usize, +} + +impl File { + pub(super) fn from_inode(inode: Inode, fs: &Ext2) -> io::Result { + if !Metadata::from_inode(&inode).file_type().is_file() { + return Err(io::Error::new(io::ErrorKind::Other, "Not a file")); + } + Ok(Self { + block_size: fs.reader.block_size, + inode, + }) + } + + /// This version of `from_inode` skips checks that the provided inode is a directory, not a file. It + /// should, only be used by `ReadDir::read_inode` to read the raw directory data. + pub(super) fn from_inode_dir(inode: Inode, fs: &Ext2) -> io::Result { + if !Metadata::from_inode(&inode).file_type().is_dir() { + return Err(io::Error::new(io::ErrorKind::Other, "Not a directory")); + } + Ok(Self { + block_size: fs.reader.block_size, + inode, + }) + } + + /// Queries metadata about the underlying file. + #[allow(unused)] + pub fn metadata(&self) -> Metadata { + Metadata::from_inode(&self.inode) + } + + pub fn read_at(&self, buf: &mut [u8], pos: u64, fs: &Ext2) -> io::Result { + //println!("File::read_at(File {{pos: {}, inode: {}, ...}}, &mut [?; {}], {})", self.pos, self.inode.number, buf.len(), pos); + let bytes_to_end = u64::from(self.inode.size_lower32).saturating_sub(pos); + if bytes_to_end == 0 { + //println!("File::read_at done, reporting 0 bytes read"); + return Ok(0); + } + let start_block = (pos / self.block_size as u64) as u32; + let offset = (pos % self.block_size as u64) as usize; + let length = core::cmp::min(bytes_to_end, buf.len() as u64); + let _ = fs + .reader + .read_block_offset( + self.inode + .log_to_phys_block(&fs.reader, start_block) + .ok_or_else(|| io::Error::new(io::ErrorKind::UnexpectedEof, "EOF reached"))?, + offset as u32, + )? + .read(buf)?; + let mut remaining_bytes = length.saturating_sub((self.block_size - offset) as u64); + for block in (start_block + 1)..=u32::MAX { + if remaining_bytes == 0 { + break; + }; + let _ = + fs.reader + .read_block(self.inode.log_to_phys_block(&fs.reader, block).ok_or_else( + || io::Error::new(io::ErrorKind::UnexpectedEof, "EOF reached"), + )?)? + .read(&mut buf[((length - remaining_bytes) as usize)..])?; + remaining_bytes = remaining_bytes.saturating_sub(self.block_size as u64); + } + //println!("File::read_at done, reporting {} bytes read", length - remaining_bytes); + Ok((length - remaining_bytes) as usize) + } +} + +#[derive(Debug)] +pub struct FileReader<'a> { + file: File, + fs: &'a Ext2, + pos: u64, +} + +impl<'a> FileReader<'a> { + pub fn new(file: File, fs: &'a Ext2) -> Self { + Self { file, fs, pos: 0 } + } +} + +impl io::Seek for FileReader<'_> { + fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + match pos { + io::SeekFrom::Start(x) => self.pos = x, + io::SeekFrom::End(x) => match x.cmp(&0) { + Ordering::Equal => self.pos = u64::from(self.file.inode.size_lower32), + Ordering::Greater => unimplemented!(), + Ordering::Less => { + self.pos = u64::from(self.file.inode.size_lower32) - x.unsigned_abs() + } + }, + io::SeekFrom::Current(x) => match x.cmp(&0) { + Ordering::Equal => (), + Ordering::Greater => self.pos += x.unsigned_abs(), + Ordering::Less => self.pos -= x.unsigned_abs(), + }, + }; + Ok(self.pos) + } +} + +impl io::Read for FileReader<'_> { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let res = self.file.read_at(buf, self.pos, &self.fs); + if let Ok(bytes_read) = res { + self.pos += bytes_read as u64; + } + res + } +} diff --git a/src/ext2/metadata.rs b/src/ext2/metadata.rs new file mode 100644 index 0000000..bc2fef1 --- /dev/null +++ b/src/ext2/metadata.rs @@ -0,0 +1,253 @@ +#![allow(unused)] +use super::structs::Inode; + +#[derive(Copy, Clone, Debug)] +pub struct Permissions(u32); + +impl Permissions { + /// Returns `true` if these permissions describe a readonly (unwritable) file. + pub fn readonly(self) -> bool { + // check if any class (owner, group, others) has write permission + self.0 & 0o222 == 0 + } + + /// Modifies the readonly flag for this set of permissions. If the + /// `readonly` argument is `true`, using the resulting `Permission` will + /// update file permissions to forbid writing. Conversely, if it's `false`, + /// using the resulting `Permission` will update file permissions to allow + /// writing. + /// + /// This operation does **not** modify the filesystem. + pub fn set_readonly(mut self, readonly: bool) { + if readonly { + // remove write permission for all classes; equivalent to `chmod a-w ` + self.0 &= !0o222; + } else { + // add write permission for all classes; equivalent to `chmod a+w ` + self.0 |= 0o222; + } + } + + /// Returns the underlying raw `st_mode` bits that contain the standard + /// Unix permissions for this file. + pub fn mode(self) -> u32 { + self.0 + } + + /// Sets the underlying raw bits for this set of permissions. + pub fn set_mode(mut self, mode: u32) { + self.0 = mode; + } + + /// Creates a new instance of `Permissions` from the given set of Unix + /// permission bits. + pub fn from_mode(mode: u32) -> Self { + Self(mode) + } +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum FileType { + Fifo, + CharDev, + Directory, + BlockDev, + File, + SymLink, + Socket, +} + +impl FileType { + /// Tests whether this file type represents a directory. The + /// result is mutually exclusive to the results of + /// [`is_file`] and [`is_symlink`]; only zero or one of these + /// tests may pass. + pub fn is_dir(self) -> bool { + self == Self::Directory + } + + /// Tests whether this file type represents a regular file. + /// The result is mutually exclusive to the results of + /// [`is_dir`] and [`is_symlink`]; only zero or one of these + /// tests may pass. + pub fn is_file(self) -> bool { + self == Self::File + } + + /// Tests whether this file type represents a symbolic link. + /// The result is mutually exclusive to the results of + /// [`is_dir`] and [`is_file`]; only zero or one of these + /// tests may pass. + pub fn is_symlink(self) -> bool { + self == Self::SymLink + } + + /// Returns `true` if this file type is a fifo. + pub fn is_fifo(self) -> bool { + self == Self::Fifo + } + + /// Returns `true` if this file type is a block device. + pub fn is_block_device(self) -> bool { + self == Self::BlockDev + } + + /// Returns `true` if this file type is a char device. + pub fn is_char_device(self) -> bool { + self == Self::CharDev + } + + /// Returns `true` if this file type is a socket. + pub fn is_socket(self) -> bool { + self == Self::Socket + } +} + +#[derive(Copy, Clone, Debug)] +pub struct Metadata { + accessed: u32, + created: u32, + file_type: FileType, + length: u64, + modified: u32, + permissions: Permissions, + ino: u64, + nlink: u64, + uid: u32, + gid: u32, + // blksize: u64, + blocks: u64, +} + +impl Metadata { + pub(super) fn from_inode(inode: &Inode) -> Self { + let file_type = match (inode.type_perms & 0xF000) >> 12 { + 1 => FileType::Fifo, + 2 => FileType::CharDev, + 4 => FileType::Directory, + 6 => FileType::BlockDev, + 8 => FileType::File, + 0xA => FileType::SymLink, + 0xC => FileType::Socket, + x => panic!("Invalid inode file type {}", x), + }; + let permissions = Permissions(u32::from(inode.type_perms & 0xFFF)); + Self { + accessed: inode.last_access_time, + created: inode.creation_time, + file_type, + length: u64::from(inode.size_lower32), + modified: inode.last_modification_time, + permissions, + ino: u64::from(inode.number), + nlink: u64::from(inode.hard_links_count), + uid: u32::from(inode.uid), + gid: u32::from(inode.gid), + // blksize: inode.reader.block_size as u64, + blocks: u64::from(inode.sectors_used), + } + } + + /// Returns the last access time of this metadata. + /// + /// The returned value corresponds to the `atime` field of `stat` + pub fn accessed(&self) -> u32 { + self.accessed + } + + /// Returns the creation time listed in this metadata. + /// + /// The returned value corresponds to the `btime` field of `statx` on + /// Linux kernel starting from to 4.11, and the `birthtime` field of `stat` on + /// other Unix platforms. + pub fn created(&self) -> u32 { + self.created + } + + /// Returns the last modification time listed in this metadata. + /// + /// The returned value corresponds to the `mtime` field of `stat` + pub fn modified(&self) -> u32 { + self.modified + } + + /// Returns the file type for this metadata. + pub fn file_type(&self) -> FileType { + self.file_type + } + + /// Returns `true` if this metadata is for a directory. The + /// result is mutually exclusive to the result of + /// [`Metadata::is_file`], and will be false for symlink metadata + /// obtained from [`symlink_metadata`]. + pub fn is_dir(&self) -> bool { + self.file_type.is_dir() + } + + /// Returns `true` if this metadata is for a regular file. The + /// result is mutually exclusive to the result of + /// [`Metadata::is_dir`], and will be false for symlink metadata + /// obtained from [`symlink_metadata`]. + /// + /// When the goal is simply to read from (or write to) the source, the most0.227s + /// reliable way to test the source can be read (or written to) is to open + /// it. Only using `is_file` can break workflows like `diff <( prog_a )` on + /// a Unix-like system for example. See [`File::open`] or + /// [`OpenOptions::open`] for more information. + pub fn is_file(&self) -> bool { + self.file_type.is_file() + } + + /// Returns `true` if this metadata is for a symbolic link. + pub fn is_symlink(&self) -> bool { + self.file_type.is_symlink() + } + + /// Returns the size of the file, in bytes, this metadata is for. + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> u64 { + self.length + } + + /// Returns the permissions of the file this metadata is for. + pub fn permissions(&self) -> Permissions { + self.permissions + } + + /// Returns the inode number. + pub fn ino(&self) -> u64 { + self.ino + } + + /// Returns the rights applied to this file. + pub fn mode(&self) -> u32 { + self.permissions.0 + } + + /// Returns the number of hard links pointing to this file. + pub fn nlink(&self) -> u64 { + self.nlink + } + + /// Returns the user ID of the owner of this file. + pub fn uid(&self) -> u32 { + self.uid + } + + /// Returns the group ID of the owner of this file. + pub fn gid(&self) -> u32 { + self.gid + } + + // /// Returns the block size for filesystem I/O. + // pub fn blksize(&self) -> u64 { + // self.blksize + // } + + /// Returns the number of blocks allocated to the file, in 512-byte units. + /// + /// Please note that this may be smaller than `st_size / 512` when the file has holes. + pub fn blocks(&self) -> u64 { + self.blocks + } +} diff --git a/src/ext2/structs.rs b/src/ext2/structs.rs new file mode 100644 index 0000000..3961dd8 --- /dev/null +++ b/src/ext2/structs.rs @@ -0,0 +1,232 @@ +use super::{block_reader::BlockReader, Ext2}; +use binread::prelude::*; +use itertools::Itertools; +use std::io::{self, Cursor, Read, Seek, SeekFrom}; +use std::vec::Vec; + +use std::fs::File as StdFile; + +#[repr(u16)] +#[derive(Debug, BinRead)] +#[br(repr=u16)] +pub enum FSState { + Clean = 1, + HasErrors = 2, +} + +#[repr(u16)] +#[derive(Debug, BinRead)] +#[br(repr=u16)] +pub enum ErrorHandlingMethod { + Ignore = 1, + RemountRO = 2, + Panic = 3, +} + +#[derive(Debug, BinRead)] +#[allow(unused)] +pub struct Superblock { + pub total_inodes: u32, + pub total_blocks: u32, + pub superuser_blocks: u32, + pub unallocated_blocks: u32, + pub unallocated_inodes: u32, + pub superblock_block: u32, + pub block_size_raw: u32, + pub fragment_size_raw: u32, + pub block_group_block_count: u32, + pub block_group_fragment_count: u32, + pub block_group_inode_count: u32, + pub last_mount_time: u32, + pub last_written_time: u32, + pub times_mounted_since_last_check: u16, + pub max_mounts_before_check: u16, + pub ext2_sig: u16, + //pub fs_state: FSState, + //pub error_handling_method: ErrorHandlingMethod, + fs_state: u16, + error_handling_method: u16, + pub version_minor: u16, + pub last_check_time: u32, + pub max_interval_between_check: u32, + pub creation_os_id: u32, + pub version_major: u32, + pub reserved_uid: u16, + pub reserved_gid: u16, +} + +impl Superblock { + pub fn read_from_disk(disk: &mut StdFile) -> io::Result { + disk.seek(SeekFrom::Start(1024))?; + let mut bytes = vec![0; 1024]; + disk.read_exact(&mut bytes)?; + let sblock = Cursor::new(bytes).read_le().unwrap(); + //dbg!(&sblock); + Ok(sblock) + } +} + +#[derive(Debug, Clone, BinRead)] +#[allow(unused)] +pub struct BlockGroupDescriptor { + pub block_usage_block: u32, + pub inode_usage_block: u32, + pub inode_table_start: u32, + pub unallocated_blocks: u16, + pub unallocated_inodes: u16, + pub num_directories: u16, + pub reserved: [u8; 14], +} + +#[derive(Debug, Clone)] +pub struct DirEntryDisk { + pub inode: u32, + pub entry_size: u16, + pub name_len: u8, + pub high_len_or_type: u8, + pub name: Vec, +} + +impl DirEntryDisk { + pub fn read(reader: &mut (impl Read + Seek)) -> io::Result { + let mut inode = [0u8; 4]; + reader.read_exact(&mut inode)?; + let inode = u32::from_le_bytes(inode); + let mut entry_size = [0u8; 2]; + reader.read_exact(&mut entry_size)?; + let entry_size = u16::from_le_bytes(entry_size); + let mut name_len = [0u8; 1]; + reader.read_exact(&mut name_len)?; + let name_len = name_len[0]; + let mut high_len_or_type = [0u8; 1]; + reader.read_exact(&mut high_len_or_type)?; + let high_len_or_type = high_len_or_type[0]; + let mut name = vec![0; name_len as usize]; + reader.read_exact(&mut name)?; + reader.seek_relative(i64::from(entry_size - u16::from(name_len) - 8))?; + //dbg!(inode, entry_size, &name); + Ok(Self { + inode, + entry_size, + name_len, + high_len_or_type, + name, + }) + } +} + +#[derive(Debug, Clone, BinRead)] +#[br(import(number: u32))] +#[allow(unused)] +pub struct Inode { + pub type_perms: u16, + pub uid: u16, + pub size_lower32: u32, + pub last_access_time: u32, + pub creation_time: u32, + pub last_modification_time: u32, + pub deletion_time: u32, + pub gid: u16, + pub hard_links_count: u16, + pub sectors_used: u32, + pub flags: u32, + pub os_specific_1: u32, + pub direct_block_pointers: [u32; 12], + pub singly_indirect_block_pointer: u32, + pub doubly_indirect_block_pointer: u32, + pub triply_indirect_block_pointer: u32, + pub generation_number: u32, + pub file_acl: u32, + pub file_size_upper32_dir_acl: u32, + pub fragment_block_address: u32, + pub os_specific_2: [u8; 12], + #[br(calc = number)] + pub number: u32, +} + +impl Inode { + pub(super) fn read(fs: &Ext2, inode_num: u32) -> io::Result { + if let Some(ino) = fs.inode_cache.read().get(&inode_num) { + //println!("Inode {}: In cache", inode_num); + return Ok(ino.clone()); + } + //println!("Inode {}: Not in cache", inode_num); + let ino = fs.descriptor_table.inode_loc(inode_num).read(&fs.reader)?; + fs.inode_cache.write().insert(inode_num, ino.clone()); + Ok(ino) + } + + pub fn pointer_block_blocks(&self, block: u32, reader: &BlockReader) -> io::Result> { + Ok(reader + .read_block(block)? + .into_inner() + .iter() + .copied() + .tuples::<(_, _, _, _)>() + .map(|raw| u32::from_le_bytes([raw.0, raw.1, raw.2, raw.3])) + .collect_vec()) + } + + pub fn log_to_phys_block(&self, reader: &BlockReader, block: u32) -> Option { + let num_sing_indir = reader.block_size / 4; + let num_doub_indir = num_sing_indir * num_sing_indir; + let num_trip_indir = num_sing_indir * num_doub_indir; + if block < 12 { + if self.direct_block_pointers[block as usize] == 0 { + None + } else { + Some(self.direct_block_pointers[block as usize]) + } + } else if block < (12 + num_sing_indir) as u32 { + let blocks = self + .pointer_block_blocks(self.singly_indirect_block_pointer, reader) + .ok()?; + let block = blocks[(block - 12) as usize]; + if block == 0 { + None + } else { + Some(block) + } + } else if block < (12 + num_sing_indir + num_doub_indir) as u32 { + let base_block = 12 - num_sing_indir; + let doub_indir_offset = (block as usize - base_block) / num_sing_indir; + let sing_indir_offset = (block as usize - base_block) % num_sing_indir; + let sing_indir_blocks = self + .pointer_block_blocks(self.doubly_indirect_block_pointer, reader) + .ok()?; + let blocks = self + .pointer_block_blocks(sing_indir_blocks[doub_indir_offset], reader) + .ok()?; + let block = blocks[sing_indir_offset]; + if block == 0 { + None + } else { + Some(block) + } + } else if block < (12 + num_sing_indir + num_doub_indir + num_trip_indir) as u32 { + let base_block = 12 - num_sing_indir; + let trip_indir_offset = (block as usize - base_block) / num_doub_indir; + let doub_indir_offset = + ((block as usize - base_block) % num_doub_indir) / num_sing_indir; + let sing_indir_offset = + ((block as usize - base_block) % num_doub_indir) % num_sing_indir; + let doub_indir_blocks = self + .pointer_block_blocks(self.triply_indirect_block_pointer, reader) + .ok()?; + let sing_indir_blocks = self + .pointer_block_blocks(doub_indir_blocks[trip_indir_offset], reader) + .ok()?; + let blocks = self + .pointer_block_blocks(sing_indir_blocks[doub_indir_offset], reader) + .ok()?; + let block = blocks[sing_indir_offset]; + if block == 0 { + None + } else { + Some(block) + } + } else { + None + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..022713c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,91 @@ +mod ext2; + +use std::{ + borrow::Cow, + fs::File, + os::mikros::{ipc, syscalls}, + sync::Arc, +}; + +use ext2::Ext2; +use parking_lot::RwLock; + +#[derive(Clone)] +struct Serv { + mounts: Arc>>, + files: Arc>>, +} + +impl fs_rpc::Server for Serv { + fn mount(&self, dev: &std::path::Path) -> Result { + let disk = File::open(dev).map_err(|_| ())?; + let fs = Ext2::new(disk).map_err(|_| ())?; + self.mounts.write().push(fs); + let res = Ok((self.mounts.read().len() - 1) as u64); + res + } + + fn open(&self, path: &std::path::Path, mount_id: u64) -> Result<(Option, u64), ()> { + let mounts = self.mounts.read(); + let mount = &mounts[mount_id as usize]; + let file = mount.open(path).map_err(|_| ())?; + self.files.write().push((file, mount_id as usize)); + let res = Ok((None, (self.files.read().len() - 1) as u64)); + res + } +} + +impl file_rpc::Server for Serv { + fn read(&self, fd: u64, pos: u64, len: usize) -> Result, ()> { + let files = self.files.read(); + let (file, mount_id) = &files[fd as usize]; + let mounts = self.mounts.read(); + let mount = &mounts[*mount_id]; + let mut buf = vec![0; len]; + let read_len = file.read_at(&mut buf, pos, mount).map_err(|_| ())?; + buf.truncate(read_len); + Ok(buf.into()) + } + + fn write(&self, _fd: u64, _pos: u64, _data: &[u8]) -> Result<(), ()> { + Err(()) + } + + fn close(&self, _fd: u64) {} + + fn size(&self, fd: u64) -> Option { + let files = self.files.read(); + let (file, _mount_id) = &files[fd as usize]; + Some(file.metadata().len()) + } +} + +fn main() { + let serv = Serv { + mounts: Arc::new(RwLock::new(Vec::new())), + files: Arc::new(RwLock::new(Vec::new())), + }; + fs_rpc::register_server(Box::new(serv.clone())); + file_rpc::register_server(Box::new(serv)); + let vfs_pid; + loop { + if let Some(pid) = syscalls::try_get_registered(0) { + vfs_pid = pid; + break; + } + } + vfs_rpc::Client::new(vfs_pid).register_fs("ext2").unwrap(); + let syslog_pid; + loop { + if let Some(pid) = syscalls::try_get_registered(2) { + syslog_pid = pid; + break; + } + } + syslog_rpc::Client::new(syslog_pid) + .send_text_binary_message("ext2".to_string(), "Ext2 FS initialized".to_string(), 0, []) + .unwrap(); + loop { + ipc::process_messages() + } +}