summaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorChuyan Zhang <me@zcy.moe>2023-10-17 23:07:21 -0700
committerChuyan Zhang <me@zcy.moe>2023-10-17 23:07:21 -0700
commit7a748cadbb2e2ce8c0e045cb8fbd77ccbd47459f (patch)
tree07ed09bb6c55110dd2f2ea59283623f023b11666 /src/main.rs
downloadmyfs-7a748cadbb2e2ce8c0e045cb8fbd77ccbd47459f.tar.gz
myfs-7a748cadbb2e2ce8c0e045cb8fbd77ccbd47459f.zip
initial commit
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs225
1 files changed, 225 insertions, 0 deletions
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();
+}