Initial commit
This commit is contained in:
commit
e205e31797
5
.cargo/config.toml
Normal file
5
.cargo/config.toml
Normal file
@ -0,0 +1,5 @@
|
||||
[build]
|
||||
target = "x86_64-unknown-mikros"
|
||||
|
||||
[install]
|
||||
root = "../os_build/sysroot"
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
427
Cargo.lock
generated
Normal file
427
Cargo.lock
generated
Normal file
@ -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"
|
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
@ -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" }
|
2
rust-toolchain.toml
Normal file
2
rust-toolchain.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "dev-x86_64-unknown-mikros"
|
80
src/ext2.rs
Normal file
80
src/ext2.rs
Normal file
@ -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<HashMap<u32, Inode>>,
|
||||
dentry_cache: RwLock<HashMap<PathBuf, DirEntryDisk>>,
|
||||
}
|
||||
|
||||
impl Ext2 {
|
||||
pub fn new(mut disk: StdFile) -> io::Result<Self> {
|
||||
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<P: AsRef<Path>>(&self, path: P) -> io::Result<u32> {
|
||||
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<P: AsRef<Path>>(&self, path: P) -> io::Result<ReadDir> {
|
||||
let path = path.as_ref();
|
||||
let inode = self.path_inode(path)?;
|
||||
ReadDir::read_inode(self, inode, path.to_owned())
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
|
||||
File::from_inode(Inode::read(self, self.path_inode(path)?)?, self)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn metadata<P: AsRef<Path>>(&self, path: P) -> io::Result<Metadata> {
|
||||
Inode::read(self, self.path_inode(path)?).map(|x| Metadata::from_inode(&x))
|
||||
}
|
||||
}
|
83
src/ext2/block_group_table.rs
Normal file
83
src/ext2/block_group_table.rs
Normal file
@ -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<BlockGroupDescriptor>,
|
||||
#[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<Self> {
|
||||
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<Inode> {
|
||||
//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");
|
||||
}
|
||||
}
|
52
src/ext2/block_reader.rs
Normal file
52
src/ext2/block_reader.rs
Normal file
@ -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<Cursor<Vec<u8>>> {
|
||||
//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<Cursor<Vec<u8>>> {
|
||||
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<Cursor<Vec<u8>>> {
|
||||
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)
|
||||
}
|
||||
}
|
170
src/ext2/dir.rs
Normal file
170
src/ext2/dir.rs
Normal file
@ -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> {
|
||||
File::from_inode(Inode::read(self.fs, self.inode)?, self.fs)
|
||||
}
|
||||
|
||||
pub fn read_dir(&self) -> io::Result<ReadDir<'a>> {
|
||||
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 <code>[`io::Result`]<[`DirEntry`]></code>. 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<FileReader<'a>>,
|
||||
directory_path: PathBuf,
|
||||
}
|
||||
|
||||
impl<'a> ReadDir<'a> {
|
||||
pub fn read_inode(fs: &'a Ext2, inode: u32, path: PathBuf) -> io::Result<ReadDir<'a>> {
|
||||
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<DirEntry<'a>> {
|
||||
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<Option<DirEntry<'a>>> {
|
||||
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<DirEntry<'a>>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.next_impl().transpose()
|
||||
}
|
||||
}
|
128
src/ext2/file.rs
Normal file
128
src/ext2/file.rs
Normal file
@ -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<Self> {
|
||||
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<Self> {
|
||||
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<usize> {
|
||||
//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<u64> {
|
||||
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<usize> {
|
||||
let res = self.file.read_at(buf, self.pos, &self.fs);
|
||||
if let Ok(bytes_read) = res {
|
||||
self.pos += bytes_read as u64;
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
253
src/ext2/metadata.rs
Normal file
253
src/ext2/metadata.rs
Normal file
@ -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 <file>`
|
||||
self.0 &= !0o222;
|
||||
} else {
|
||||
// add write permission for all classes; equivalent to `chmod a+w <file>`
|
||||
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
|
||||
}
|
||||
}
|
232
src/ext2/structs.rs
Normal file
232
src/ext2/structs.rs
Normal file
@ -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<Self> {
|
||||
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<u8>,
|
||||
}
|
||||
|
||||
impl DirEntryDisk {
|
||||
pub fn read(reader: &mut (impl Read + Seek)) -> io::Result<Self> {
|
||||
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<Self> {
|
||||
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<Vec<u32>> {
|
||||
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<u32> {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
91
src/main.rs
Normal file
91
src/main.rs
Normal file
@ -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<RwLock<Vec<Ext2>>>,
|
||||
files: Arc<RwLock<Vec<(ext2::File, usize)>>>,
|
||||
}
|
||||
|
||||
impl fs_rpc::Server for Serv {
|
||||
fn mount(&self, dev: &std::path::Path) -> Result<u64, ()> {
|
||||
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>, 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<Cow<'_, [u8]>, ()> {
|
||||
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<u64> {
|
||||
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()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user