From 7a748cadbb2e2ce8c0e045cb8fbd77ccbd47459f Mon Sep 17 00:00:00 2001 From: Chuyan Zhang Date: Tue, 17 Oct 2023 23:07:21 -0700 Subject: initial commit --- .gitignore | 1 + Cargo.lock | 459 ++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 14 ++ src/block_device/memory_disk.rs | 34 +++ src/block_device/mod.rs | 7 + src/disk/bitmap.rs | 49 +++++ src/disk/inode.rs | 152 +++++++++++++ src/disk/mod.rs | 2 + src/main.rs | 225 ++++++++++++++++++++ 9 files changed, 943 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/block_device/memory_disk.rs create mode 100644 src/block_device/mod.rs create mode 100644 src/disk/bitmap.rs create mode 100644 src/disk/inode.rs create mode 100644 src/disk/mod.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..064517b --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,459 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "clap" +version = "4.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fuser" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21370f84640642c8ea36dfb2a6bfc4c55941f476fcf431f6fef25a5ddcf0169b" +dependencies = [ + "libc", + "log", + "memchr", + "page_size", + "pkg-config", + "smallvec", + "zerocopy", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys", +] + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "linux-raw-sys" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "myfs" +version = "0.1.0" +dependencies = [ + "bitflags", + "clap", + "env_logger", + "fuser", + "libc", + "log", +] + +[[package]] +name = "page_size" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b7663cbd190cfd818d08efa8497f6cd383076688c49a391ef7c0d03cd12b561" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rustix" +version = "0.38.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "zerocopy" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20707b61725734c595e840fb3704378a0cd2b9c74cc9e6e20724838fc6a1e2f9" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56097d5b91d711293a42be9289403896b68654625021732067eac7a4ca388a1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4a4b6f3 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "myfs" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bitflags = "2.4.0" +clap = { version = "4.4.6", features = ["derive"] } +env_logger = "0.10.0" +fuser = "0.13.0" +libc = "0.2.148" +log = "0.4.20" diff --git a/src/block_device/memory_disk.rs b/src/block_device/memory_disk.rs new file mode 100644 index 0000000..4cffaf2 --- /dev/null +++ b/src/block_device/memory_disk.rs @@ -0,0 +1,34 @@ +use std::cell::RefCell; +use crate::block_device::{BLOCK_SIZE, BlockDevice}; + +#[repr(C)] +pub struct MemoryDisk { + /// Emulating a block device with a segment of RAM, + /// which is 64MiB == 4KiB per block * 16384 blocks + pub arena: RefCell>, +} + +impl MemoryDisk { + pub fn new() -> Self { + Self { + arena: RefCell::new(vec![0u8; BLOCK_SIZE * 16384]), + } + } + +} + +impl BlockDevice for MemoryDisk { + fn read(&self, block_id: usize, buffer: &mut [u8]) { + let block_front = block_id * BLOCK_SIZE; + let block_back = block_front + BLOCK_SIZE; + let arena = self.arena.borrow(); + buffer.copy_from_slice(&arena[block_front..block_back]); + } + + fn write(&self, block_id: usize, buffer: &[u8]) { + let block_front = block_id * BLOCK_SIZE; + let block_back = block_front + BLOCK_SIZE; + let mut arena = self.arena.borrow_mut(); + arena[block_front..block_back].copy_from_slice(buffer); + } +} \ No newline at end of file diff --git a/src/block_device/mod.rs b/src/block_device/mod.rs new file mode 100644 index 0000000..9d52e53 --- /dev/null +++ b/src/block_device/mod.rs @@ -0,0 +1,7 @@ +pub mod memory_disk; + +pub const BLOCK_SIZE: usize = 4096; +pub trait BlockDevice { + fn read(&self, block_id: usize, buffer: &mut [u8]); + fn write(&self, block_id: usize, buffer: &[u8]); +} diff --git a/src/disk/bitmap.rs b/src/disk/bitmap.rs new file mode 100644 index 0000000..a5a9cc4 --- /dev/null +++ b/src/disk/bitmap.rs @@ -0,0 +1,49 @@ +use crate::block_device::{BlockDevice, BLOCK_SIZE}; +use std::sync::Arc; + +pub struct Bitmap { + pub starting_block: usize, + pub length: usize, + pub device: Arc, + pub data: Vec, + pub dirty_blocks: Vec, +} + +impl Bitmap { + pub fn new(starting_block: usize, length: usize, device: Arc) -> Self { + Self { + starting_block, + length, + device, + data: vec![0u8; length * BLOCK_SIZE], + dirty_blocks: Vec::new(), + } + } + pub fn allocate(&mut self) -> usize { + for (i, byte) in self.data.iter_mut().enumerate() { + let leading_ones = byte.leading_ones(); + if leading_ones != 8 { + *byte |= (1 << (7 - leading_ones)) as u8; + self.dirty_blocks.push(i / BLOCK_SIZE); + return i * 8 + leading_ones as usize; + } + } + panic!("No more space for allocation!") + } + + pub fn query(&self, index: usize) -> bool { + self.data[index / 8] & ((1 << (7 - index % 8)) as u8) != 0 + } + + fn write_back(&mut self) { + for block_index_offset in self.dirty_blocks.iter() { + let buffer_front_index = BLOCK_SIZE * block_index_offset; + let buffer_back_index = BLOCK_SIZE * (block_index_offset + 1); + self.device.write( + self.starting_block + block_index_offset, + &self.data[buffer_front_index..buffer_back_index], + ); + } + self.dirty_blocks = Vec::new(); + } +} diff --git a/src/disk/inode.rs b/src/disk/inode.rs new file mode 100644 index 0000000..f38508a --- /dev/null +++ b/src/disk/inode.rs @@ -0,0 +1,152 @@ +use bitflags::bitflags; + +const DIRECT_NUMBER: usize = 15; + +#[derive(Debug)] +pub struct InodeMode(u16); + +bitflags! { + impl InodeMode: u16 { + const IXOTH = 0x0001; + const IWOTH = 0x0002; + const IROTH = 0x0004; + const IXGRP = 0x0008; + const IWGRP = 0x0010; + const IRGRP = 0x0020; + const IXUSR = 0x0040; + const IWUSR = 0x0080; + const IRUSR = 0x0100; + const ISVTX = 0x0200; + const ISGID = 0x0400; + const ISUID = 0x0800; + // These are mutually-exclusive: + const IFIFO = 0x1000; + const IFCHR = 0x2000; + const IFDIR = 0x4000; + const IFBLK = 0x6000; + const IFREG = 0x8000; + const IFLNK = 0xA000; + const IFSOCK = 0xC000; + } +} + +/// Pretty much the same with ext2, with minor changes: +/// - removed OS dependent attributes (osd1 & osd2) +/// - removed i_faddr since fragmentation is not supported +/// - changed uid and gid from u16 to u32 +/// - added more direct blocks for a total size of 128 bytes +/// TODO: do we need to extend time precision? +#[repr(C)] +#[derive(Debug)] +pub struct Inode { + mode: InodeMode, + uid: u32, + size: u32, + atime: u32, // time in seconds + ctime: u32, + mtime: u32, + dtime: u32, + gid: u32, + n_links: u16, + n_blocks: u32, + flags: u32, // TODO: do we actually need this? maybe just return 0 + direct: [u32; DIRECT_NUMBER], + single_indirect: u32, + double_indirect: u32, + triple_indirect: u32, + generation: u32, + file_acl: u32, + dir_acl: u32, // TODO do we have to implement ACL......? +} + +impl Inode { + pub fn directory() -> Self { + Self { + mode: InodeMode::IFDIR + | InodeMode::IRUSR + | InodeMode::IWUSR + | InodeMode::IXUSR + | InodeMode::IRGRP + | InodeMode::IXGRP + | InodeMode::IROTH + | InodeMode::IXOTH, + // Directory, 755 permissions + uid: 0, + size: 0, + atime: 0, + ctime: 0, + mtime: 0, + dtime: 0, + gid: 0, + n_links: 0, + n_blocks: 0, + flags: 0, + direct: [0; DIRECT_NUMBER], + single_indirect: 0, + double_indirect: 0, + triple_indirect: 0, + generation: 0, + file_acl: 0, + dir_acl: 0, + } + } + + pub fn file() -> Self { + Self { + mode: InodeMode::IFREG + | InodeMode::IRUSR + | InodeMode::IWUSR + | InodeMode::IXUSR + | InodeMode::IRGRP + | InodeMode::IXGRP + | InodeMode::IROTH + | InodeMode::IXOTH, + // RegularFile, 755 permissions + uid: 0, + size: 0, + atime: 0, + ctime: 0, + mtime: 0, + dtime: 0, + gid: 0, + n_links: 0, + n_blocks: 0, + flags: 0, + direct: [0; DIRECT_NUMBER], + single_indirect: 0, + double_indirect: 0, + triple_indirect: 0, + generation: 0, + file_acl: 0, + dir_acl: 0, + } + } +} + +// +// #[repr(C)] +// #[derive(Debug, Default)] +// pub struct FileInode { +// file_size: u32, +// direct_blocks: [u32; DIRECT_NUMBER], +// indirect_block: u32, +// doubly_indirect_block: u32, +// } // sizeof(FileInode) == 124 bytes +// +// #[repr(C)] +// #[derive(Debug, Default)] +// pub struct DirectoryInode { +// child_number: u32, +// direct_blocks: [u32; DIRECT_NUMBER], +// indirect_block: u32, +// doubly_indirect_block: u32, +// } // sizeof(FileInode) == 124 bytes +// +// #[repr(C)] +// #[derive(Debug)] +// pub enum Inode { +// File(FileInode), +// Directory(DirectoryInode), +// } // sizeof(Inode) == 128 bytes + +pub const INODE_SIZE: usize = std::mem::size_of::(); diff --git a/src/disk/mod.rs b/src/disk/mod.rs new file mode 100644 index 0000000..e4e4216 --- /dev/null +++ b/src/disk/mod.rs @@ -0,0 +1,2 @@ +pub mod inode; +pub mod bitmap; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..414baaf --- /dev/null +++ b/src/main.rs @@ -0,0 +1,225 @@ +mod block_device; +mod disk; + +use clap::Parser; +use fuser::{Filesystem, MountOption, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry, Request}; +use std::ffi::OsStr; +use std::sync::Arc; +use std::time::Duration; +use log::debug; + +use block_device::{memory_disk::MemoryDisk, BlockDevice, BLOCK_SIZE}; +use disk::bitmap::Bitmap; +use disk::inode::{Inode, INODE_SIZE}; +use libc::ENOENT; + +#[derive(Parser, Debug)] +#[command(author, version, about)] +struct Args { + mount_point: Option, + #[arg(long)] + auto_unmount: bool, + #[arg(long)] + allow_root: bool, +} + +const TTL: Duration = Duration::from_secs(1); +const INODE_PER_BLOCK: usize = BLOCK_SIZE / INODE_SIZE; + +/// The design of MyFS is rather simple: +/// +-------------------+ +/// | Super Block | +/// +-------------------+ +/// | Inode Bitmap | +/// +-------------------+ +/// | ... | +/// +-------------------+ +/// | Data Block Bitmap | +/// +-------------------+ +/// | ... | +/// +-------------------+ +/// | Inode Block | +/// +-------------------+ +/// | ... | +/// +-------------------+ +/// | Data Block | +/// +-------------------+ +/// With each block 4KiB, each Inode entry 128B + +#[repr(C)] +struct MyFS { + device: Arc, + data_bitmap: Bitmap, + inode_bitmap: Bitmap, + inode_start_block: usize, + data_start_block: usize, +} + +impl MyFS { + fn new(device: Arc, total_block_number: usize) -> Self { + let max_inode_number: usize = 16384; // TODO: remove hard-coded magic number + let inode_block_number = max_inode_number / INODE_PER_BLOCK; // == 128 + let inode_bitmap_block_number = (inode_block_number + BLOCK_SIZE - 1) / BLOCK_SIZE; + + let blocks_remaining = + total_block_number - inode_block_number - inode_bitmap_block_number - 1; + // let number of data blocks be x, the remaining block number be C, + // the corresponding data bitmap length should be ceil(x / BLK_SIZE), + // thus we have BLK_SIZE * (C-1) / (BLK_SIZE+1) <= x <= BLK_SIZE * C / (BLK_SIZE+1) + // the difference of the two bounds is less than 1, + // meaning only 1 integer could be in between. + // Thus we have x = floor(BLK_SIZE * C / (BLK_SIZE + 1)) + let data_block_number = BLOCK_SIZE * blocks_remaining / (BLOCK_SIZE + 1); + let data_bitmap_block_number = blocks_remaining - data_block_number; + debug!("dbbn: {}", data_bitmap_block_number); + debug!("ibbn: {}", inode_bitmap_block_number); + debug!("ibn: {}", inode_block_number); + debug!("dbn: {}", data_block_number); + debug!("sum: {}", 1 + data_bitmap_block_number + inode_bitmap_block_number + inode_block_number + data_block_number); + + let mut data_bitmap = Bitmap::new(1, data_bitmap_block_number, device.clone()); + let mut inode_bitmap = Bitmap::new( + data_bitmap_block_number + 1, + inode_bitmap_block_number, + device.clone(), + ); + let mut fs = Self { + device, + data_bitmap, + inode_bitmap, + inode_start_block: data_bitmap_block_number + inode_bitmap_block_number + 1, + data_start_block: data_bitmap_block_number + + inode_bitmap_block_number + + inode_block_number + + 1, + }; + + let _ = fs.inode_bitmap.allocate(); // Inode starts from 1 + let root_inode_index = fs.inode_bitmap.allocate(); + assert_eq!(root_inode_index, 1); + let (root_inode_block, root_inode_offset) = fs.locate_inode(root_inode_index); + fs.put_inode(root_inode_block, root_inode_offset, Inode::directory()); + + fs + } + + // pub fn allocate_inode(&mut self) -> usize { + // self.inode_bitmap.allocate() + // } + + pub fn inode_active(&self, inode: usize) -> bool { + self.inode_bitmap.query(inode) + } + + /// 输入 inode 编号, 返回它对应的 block number 和 block 内 offset + pub fn locate_inode(&self, inode: usize) -> (usize, usize) { + let block_number = + inode / INODE_PER_BLOCK + 1 + self.inode_bitmap.length + self.data_bitmap.length; + let block_offset = inode % INODE_PER_BLOCK * INODE_SIZE; + (block_number, block_offset) + } + + // TODO: 实现一个 LRU 的 cache 机制, 不要每次都开 buffer + pub fn put_inode(&mut self, block: usize, offset: usize, inode: Inode) { + let mut buffer = vec![0u8; BLOCK_SIZE]; + self.device.read(block, buffer.as_mut_slice()); + + let inode_raw = &inode as *const Inode as *const u8; + let inode_slice = unsafe { std::slice::from_raw_parts(inode_raw, INODE_SIZE) }; + buffer[offset..offset + INODE_SIZE].copy_from_slice(inode_slice); + + self.device.write(block, buffer.as_slice()); + } + + // TODO: 实现一个 LRU 的 cache 机制, 不要每次都开 buffer + pub fn get_inode(&self, block: usize, offset: usize) -> Inode { + let mut buffer = vec![0u8; BLOCK_SIZE]; + self.device.read(block, buffer.as_mut_slice()); + + let inode = Inode::file(); + let inode_slice = unsafe { + std::slice::from_raw_parts_mut(&inode as *const Inode as *mut u8, INODE_SIZE) + }; + inode_slice.copy_from_slice(&buffer[offset..offset + INODE_SIZE]); + inode + } +} + +impl Filesystem for MyFS { + fn lookup(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) { + debug!( + "Filesystem::lookup called with parent {} name {}", + parent, + name.to_str().unwrap() + ); + let parent = parent as usize; + if self.inode_active(parent) { + let (block, offset) = self.locate_inode(parent); + let inode = self.get_inode(block, offset); + debug!("{:?}", inode); + } + reply.error(ENOENT); + } + + fn getattr(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyAttr) { + debug!("Filesystem::getattr called with ino {}", ino); + let ino = ino as usize; + if self.inode_active(ino) { + let (block, offset) = self.locate_inode(ino); + let inode = self.get_inode(block, offset); + debug!("{:?}", inode); + } + reply.error(ENOENT); + } + + fn read( + &mut self, + _req: &Request<'_>, + ino: u64, + _fh: u64, + offset: i64, + _size: u32, + _flags: i32, + _lock_owner: Option, + reply: ReplyData, + ) { + todo!() + } + + fn readdir( + &mut self, + _req: &Request<'_>, + ino: u64, + _fh: u64, + offset: i64, + mut reply: ReplyDirectory, + ) { + todo!() + } +} + +fn main() { + env_logger::init(); + let args = Args::parse(); + let mount_point = args.mount_point.unwrap(); + // let mut options = vec![ + // MountOption::RO, + // MountOption::FSName("hello".to_string()), + // ]; + // if args.allow_root { + // options.push(MountOption::AutoUnmount); + // } + // if args.allow_root { + // options.push(MountOption::AllowRoot); + // } + let options = vec![ + MountOption::RO, + MountOption::FSName("hello".to_string()), + MountOption::AutoUnmount, + MountOption::AllowRoot, + ]; + let mem_disk = Arc::new(MemoryDisk::new()); + let filesystem = MyFS::new(mem_disk, 16384); + + fuser::mount2(filesystem, mount_point, &options).unwrap(); +} -- cgit v1.2.3-70-g09d2