Ver Fonte

feat(fs): add ext4 fs support based on ext4_rs crate

Heinz há 7 meses atrás
pai
commit
77e6bd8f0c
6 ficheiros alterados com 260 adições e 0 exclusões
  1. 11 0
      Cargo.lock
  2. 1 0
      Cargo.toml
  3. 242 0
      src/fs/ext4.rs
  4. 1 0
      src/fs/mod.rs
  5. 4 0
      src/kernel/block.rs
  6. 1 0
      src/lib.rs

+ 11 - 0
Cargo.lock

@@ -145,6 +145,7 @@ dependencies = [
  "eonix_preempt",
  "eonix_runtime",
  "eonix_sync",
+ "ext4_rs",
  "intrusive-collections",
  "intrusive_list",
  "itertools",
@@ -247,6 +248,16 @@ dependencies = [
  "intrusive-collections",
 ]
 
+[[package]]
+name = "ext4_rs"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a1a97344bde15b0ace15e265dab27228d4bdc37a0bfa8548c5645d7cfa6a144"
+dependencies = [
+ "bitflags",
+ "log",
+]
+
 [[package]]
 name = "fdt"
 version = "0.1.5"

+ 1 - 0
Cargo.toml

@@ -30,6 +30,7 @@ itertools = { version = "0.13.0", default-features = false }
 acpi = "5.2.0"
 align_ext = "0.1.0"
 xmas-elf = "0.10.0"
+ext4_rs = "1.3.2"
 
 [target.'cfg(target_arch = "riscv64")'.dependencies]
 virtio-drivers = { version = "0.11.0" }

+ 242 - 0
src/fs/ext4.rs

@@ -0,0 +1,242 @@
+use core::sync::atomic::Ordering;
+use alloc::{collections::btree_map::BTreeMap, sync::{Arc, Weak}};
+use ext4_rs::{Ext4, InodeFileType};
+use ext4_rs::BlockDevice as Ext4BlockDeviceTrait;
+
+use crate::{
+    io::{Buffer, ByteBuffer},
+    kernel::{
+        block::{make_device, BlockDevice},
+        constants::{EIO, S_IFDIR, S_IFREG},
+        vfs::{
+            dentry::Dentry,
+            inode::{define_struct_inode, Ino, Inode, InodeData},
+            mount::{register_filesystem, Mount, MountCreator},
+            vfs::Vfs,
+            DevId,
+        },
+    }, prelude::*
+};
+
+pub struct Ext4BlockDevice {
+    device: Arc<BlockDevice>,
+    block_size: u64,
+}
+
+impl Ext4BlockDevice {
+    pub fn new(device: Arc<BlockDevice>, block_size: u64) -> Self {
+        Self {
+            device,
+            block_size,
+        }
+    }
+}
+
+impl Ext4BlockDeviceTrait for Ext4BlockDevice {
+    fn read_offset(&self, offset: usize) -> Vec<u8> {
+        let mut buffer = vec![0u8; self.block_size as usize];
+        let mut byte_buffer = ByteBuffer::new(buffer.as_mut_slice());
+        match self.device.read_some(offset, &mut byte_buffer) {
+            Ok(fill_result) => {
+                buffer
+            }
+            Err(_) => {
+                vec![0u8; self.block_size as usize]
+            }
+        }
+    }
+
+    fn write_offset(&self, offset: usize, data: &[u8]) {
+        todo!()
+    }
+}
+
+impl_any!(Ext4Fs);
+struct Ext4Fs {
+    inner: Ext4,
+    device: Arc<BlockDevice>,
+    icache: BTreeMap<Ino, Ext4Inode>,
+    weak: Weak<Ext4Fs>,
+}
+
+impl Vfs for Ext4Fs {
+    fn io_blksize(&self) -> usize {
+        4096
+    }
+
+    fn fs_devid(&self) -> DevId {
+        self.device.devid()
+    }
+
+    fn is_read_only(&self) -> bool {
+        true
+    }
+}
+
+impl Ext4Fs {
+    fn get_or_alloc_inode(&self, ino: Ino, is_directory: bool, size: u32) -> Arc<dyn Inode> {
+        self.icache
+            .get(&ino)
+            .cloned()
+            .map(Ext4Inode::unwrap)
+            .unwrap_or_else(|| {
+                if is_directory {
+                    DirInode::new(ino, self.weak.clone(), size)
+                } else {
+                    FileInode::new(ino, self.weak.clone(), size)
+                }
+            })
+    }
+}
+
+impl Ext4Fs {
+    pub fn create(device: DevId) -> KResult<(Arc<Self>, Arc<dyn Inode>)>  {
+        let device = BlockDevice::get(device)?;
+        let device_size = device.size();
+        let ext4_device = Ext4BlockDevice::new(device.clone(), device_size);
+        let ext4 = Ext4::open(Arc::new(ext4_device));
+        let mut ext4fs = Arc::new_cyclic(|weak: &Weak<Ext4Fs>| Self {
+            inner: ext4,
+            device,
+            icache: BTreeMap::new(),
+            weak: weak.clone(),
+        });
+        
+        let root_inode_ref = ext4fs.inner.get_inode_ref(2);
+        let root_inode = DirInode::new(root_inode_ref.inode_num as Ino, ext4fs.weak.clone(), root_inode_ref.inode.size() as u32);
+        Ok((ext4fs, root_inode))
+    }
+}
+
+#[allow(dead_code)]
+#[derive(Clone)]
+enum Ext4Inode {
+    File(Arc<FileInode>),
+    Dir(Arc<DirInode>),
+}
+
+impl Ext4Inode {
+    fn unwrap(self) -> Arc<dyn Inode> {
+        match self {
+            Ext4Inode::File(inode) => inode,
+            Ext4Inode::Dir(inode) => inode,
+        }
+    }
+}
+
+define_struct_inode! {
+    struct FileInode;
+}
+
+impl FileInode {
+    pub fn new(ino: Ino, weak: Weak<dyn Vfs>, size: u32) -> Arc<Self> {
+        let inode = Arc::new(Self {
+            idata: InodeData::new(ino, weak),
+        });
+        inode.nlink.store(1, Ordering::Relaxed);
+        inode.mode.store(S_IFREG | 0o644, Ordering::Relaxed);
+        inode.size.store(size as u64, Ordering::Relaxed);
+
+        inode
+    }
+}
+
+impl Inode for FileInode {
+    fn read(&self, buffer: &mut dyn Buffer, offset: usize) -> KResult<usize> {
+        let vfs = self.vfs.upgrade().ok_or(EIO)?;
+        let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
+        
+        let mut temp_buf = vec![0u8; buffer.total()];
+        match ext4fs.inner.read_at(self.ino.try_into().unwrap(), offset, &mut temp_buf) {
+            Ok(bytes_read) => {
+                buffer.fill(&temp_buf[..bytes_read]).map_err(|_| EIO)?;
+                Ok(bytes_read)
+            }
+            Err(e) => Err(e.error() as u32),
+        }
+    }
+}
+
+define_struct_inode! {
+    struct DirInode;
+}
+
+impl DirInode {
+    pub fn new(ino: Ino, weak: Weak<dyn Vfs>, size: u32) -> Arc<Self> {
+        let inode = Arc::new(Self {
+            idata: InodeData::new(ino, weak),
+        });
+        inode.nlink.store(2, Ordering::Relaxed);
+        inode.mode.store(S_IFDIR | 0o644, Ordering::Relaxed);
+        inode.size.store(size as u64, Ordering::Relaxed);
+
+        inode
+    }
+}
+
+impl Inode for DirInode {
+    fn lookup(&self, dentry: &Arc<Dentry>) -> KResult<Option<Arc<dyn Inode>>> {
+        let vfs = self.vfs.upgrade().ok_or(EIO)?;
+        let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
+
+        let name = str::from_utf8(&**dentry.name()).unwrap();
+        match ext4fs.inner.fuse_lookup(self.ino, name) {
+            Ok(attr) => {
+                let new_ino = attr.ino as Ino;
+                let is_dir = attr.kind == InodeFileType::S_IFDIR;
+                let size = attr.size;
+
+                let inode = ext4fs.get_or_alloc_inode(new_ino, is_dir, size as u32);
+                Ok(Some(inode))
+            }
+            Err(e) => Err(e.error() as u32),
+        }
+    }
+
+    fn do_readdir(
+            &self,
+            offset: usize,
+            callback: &mut dyn FnMut(&[u8], Ino) -> KResult<core::ops::ControlFlow<(), ()>>,
+        ) -> KResult<usize> {
+        let vfs = self.vfs.upgrade().ok_or(EIO)?;
+        let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
+
+        let entries = ext4fs.inner.fuse_readdir(self.ino as u64, 0, offset as i64)
+            .map_err(|_| EIO)?;
+        let mut nread = 0usize;
+
+        for entry in entries {
+            let name_len = entry.name_len as usize;
+            let name = &entry.name[..name_len];
+
+            let ino = entry.inode as Ino;
+            let inode_ref = ext4fs.inner.get_inode_ref(entry.inode);
+
+            let is_dir = inode_ref.inode.is_dir();
+            let size = inode_ref.inode.size();
+
+            ext4fs.get_or_alloc_inode(ino, is_dir, size as u32);
+
+            if callback(name, ino)?.is_break() {
+                break;
+            }
+
+            nread += entry.entry_len as usize;
+        }
+        Ok(nread)
+    }
+}
+
+struct Ext4MountCreator;
+
+impl MountCreator for Ext4MountCreator {
+    fn create_mount(&self, _source: &str, _flags: u64, mp: &Arc<Dentry>) -> KResult<Mount> {
+        let (ext4fs, root_inode) = Ext4Fs::create(make_device(8, 1))?;
+
+        Mount::new(mp, ext4fs, root_inode)
+    }
+}
+
+pub fn init() {
+    register_filesystem("ext4", Arc::new(Ext4MountCreator)).unwrap();
+}

+ 1 - 0
src/fs/mod.rs

@@ -1,3 +1,4 @@
 pub mod fat32;
 pub mod procfs;
 pub mod tmpfs;
+pub mod ext4;

+ 4 - 0
src/kernel/block.rs

@@ -281,6 +281,10 @@ impl BlockDevice {
             Ok(FillResult::Partial(nfilled))
         }
     }
+
+    pub fn size(&self) -> u64 {
+        self.size
+    }
 }
 
 pub struct BlockDeviceRequest<'lt> {

+ 1 - 0
src/lib.rs

@@ -143,6 +143,7 @@ async fn init_process(early_kstack: PRange) {
     fs::tmpfs::init();
     fs::procfs::init();
     fs::fat32::init();
+    fs::ext4::init();
 
     let load_info = {
         // mount fat32 /mnt directory