summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock459
-rw-r--r--Cargo.toml14
-rw-r--r--src/block_device/memory_disk.rs34
-rw-r--r--src/block_device/mod.rs7
-rw-r--r--src/disk/bitmap.rs49
-rw-r--r--src/disk/inode.rs152
-rw-r--r--src/disk/mod.rs2
-rw-r--r--src/main.rs225
9 files changed, 943 insertions, 0 deletions
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<Vec<u8>>,
+}
+
+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<dyn BlockDevice>,
+ pub data: Vec<u8>,
+ pub dirty_blocks: Vec<usize>,
+}
+
+impl Bitmap {
+ pub fn new(starting_block: usize, length: usize, device: Arc<dyn BlockDevice>) -> 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::<Inode>();
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<String>,
+ #[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<dyn BlockDevice>,
+ data_bitmap: Bitmap,
+ inode_bitmap: Bitmap,
+ inode_start_block: usize,
+ data_start_block: usize,
+}
+
+impl MyFS {
+ fn new(device: Arc<dyn BlockDevice>, 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<u64>,
+ 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();
+}