Explorar o código

change(ext4): rework some part of the ext4 filesystem

greatbridf hai 7 meses
pai
achega
65b3b66088
Modificáronse 6 ficheiros con 206 adicións e 176 borrados
  1. 174 103
      src/fs/ext4.rs
  2. 8 0
      src/fs/fat32.rs
  3. 4 0
      src/fs/procfs.rs
  4. 4 0
      src/fs/tmpfs.rs
  5. 15 73
      src/kernel/block.rs
  6. 1 0
      src/kernel/vfs/mount.rs

+ 174 - 103
src/fs/ext4.rs

@@ -1,22 +1,30 @@
-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 core::sync::atomic::{AtomicU32, AtomicU64};
 
 use crate::{
     io::{Buffer, ByteBuffer},
     kernel::{
-        block::{make_device, BlockDevice},
-        constants::{EIO, S_IFDIR, S_IFREG},
+        block::BlockDevice,
+        constants::EIO,
         vfs::{
             dentry::Dentry,
-            inode::{define_struct_inode, Ino, Inode, InodeData},
+            inode::{define_struct_inode, AtomicNlink, Ino, Inode, InodeData},
             mount::{register_filesystem, Mount, MountCreator},
+            s_isdir, s_isreg,
             vfs::Vfs,
-            DevId,
+            DevId, FsContext, TimeSpec,
         },
-    }, prelude::*
+    },
+    path::Path,
+    prelude::*,
 };
+use alloc::{
+    collections::btree_map::{BTreeMap, Entry},
+    sync::Arc,
+};
+use eonix_runtime::task::Task;
+use eonix_sync::RwLock;
+use ext4_rs::{BlockDevice as Ext4BlockDeviceTrait, Ext4Error};
+use ext4_rs::{Errno, Ext4};
 
 pub struct Ext4BlockDevice {
     device: Arc<BlockDevice>,
@@ -24,9 +32,7 @@ pub struct Ext4BlockDevice {
 
 impl Ext4BlockDevice {
     pub fn new(device: Arc<BlockDevice>) -> Self {
-        Self {
-            device,
-        }
+        Self { device }
     }
 }
 
@@ -34,17 +40,16 @@ impl Ext4BlockDeviceTrait for Ext4BlockDevice {
     fn read_offset(&self, offset: usize) -> Vec<u8> {
         let mut buffer = vec![0u8; 4096];
         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; 4096]
-            }
-        }
+
+        let _ = self
+            .device
+            .read_some(offset, &mut byte_buffer)
+            .expect("Failed to read from block device");
+
+        buffer
     }
 
-    fn write_offset(&self, offset: usize, data: &[u8]) {
+    fn write_offset(&self, _offset: usize, _data: &[u8]) {
         todo!()
     }
 }
@@ -53,8 +58,7 @@ impl_any!(Ext4Fs);
 struct Ext4Fs {
     inner: Ext4,
     device: Arc<BlockDevice>,
-    icache: BTreeMap<Ino, Ext4Inode>,
-    weak: Weak<Ext4Fs>,
+    icache: RwLock<BTreeMap<Ino, Ext4Inode>>,
 }
 
 impl Vfs for Ext4Fs {
@@ -72,39 +76,87 @@ impl Vfs for Ext4Fs {
 }
 
 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)
+    fn try_get(&self, icache: &BTreeMap<Ino, Ext4Inode>, ino: u64) -> Option<Arc<dyn Inode>> {
+        icache.get(&ino).cloned().map(Ext4Inode::into_inner)
+    }
+
+    fn get_or_insert(
+        &self,
+        icache: &mut BTreeMap<Ino, Ext4Inode>,
+        mut idata: InodeData,
+    ) -> Arc<dyn Inode> {
+        match icache.entry(idata.ino) {
+            Entry::Occupied(occupied) => occupied.get().clone().into_inner(),
+            Entry::Vacant(vacant) => {
+                let mode = *idata.mode.get_mut();
+                if s_isreg(mode) {
+                    vacant
+                        .insert(Ext4Inode::File(Arc::new(FileInode { idata })))
+                        .clone()
+                        .into_inner()
+                } else if s_isdir(mode) {
+                    vacant
+                        .insert(Ext4Inode::Dir(Arc::new(DirInode { idata })))
+                        .clone()
+                        .into_inner()
                 } else {
-                    FileInode::new(ino, self.weak.clone(), size)
+                    println_warn!("ext4: Unsupported inode type: {mode:#o}");
+                    vacant
+                        .insert(Ext4Inode::File(Arc::new(FileInode { idata })))
+                        .clone()
+                        .into_inner()
                 }
-            })
+            }
+        }
     }
 }
 
 impl Ext4Fs {
-    pub fn create(device: DevId) -> KResult<(Arc<Self>, Arc<dyn Inode>)>  {
-        let device = BlockDevice::get(device)?;
+    pub fn create(device: Arc<BlockDevice>) -> KResult<(Arc<Self>, Arc<dyn Inode>)> {
         let ext4_device = Ext4BlockDevice::new(device.clone());
         let ext4 = Ext4::open(Arc::new(ext4_device));
-        let mut ext4fs = Arc::new_cyclic(|weak: &Weak<Ext4Fs>| Self {
+
+        let ext4fs = Arc::new(Self {
             inner: ext4,
             device,
-            icache: BTreeMap::new(),
-            weak: weak.clone(),
+            icache: RwLock::new(BTreeMap::new()),
         });
-        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);
+
+        let root_inode = {
+            let mut icache = Task::block_on(ext4fs.icache.write());
+            let root_inode = ext4fs.inner.get_inode_ref(2);
+
+            ext4fs.get_or_insert(
+                &mut icache,
+                InodeData {
+                    ino: root_inode.inode_num as Ino,
+                    size: AtomicU64::new(root_inode.inode.size()),
+                    nlink: AtomicNlink::new(root_inode.inode.links_count() as _),
+                    uid: AtomicU32::new(root_inode.inode.uid() as _),
+                    gid: AtomicU32::new(root_inode.inode.gid() as _),
+                    mode: AtomicU32::new(root_inode.inode.mode() as _),
+                    atime: Spin::new(TimeSpec {
+                        sec: root_inode.inode.atime() as _,
+                        nsec: root_inode.inode.i_atime_extra() as _,
+                    }),
+                    ctime: Spin::new(TimeSpec {
+                        sec: root_inode.inode.ctime() as _,
+                        nsec: root_inode.inode.i_ctime_extra() as _,
+                    }),
+                    mtime: Spin::new(TimeSpec {
+                        sec: root_inode.inode.mtime() as _,
+                        nsec: root_inode.inode.i_mtime_extra() as _,
+                    }),
+                    rwsem: RwLock::new(()),
+                    vfs: Arc::downgrade(&ext4fs) as _,
+                },
+            )
+        };
+
         Ok((ext4fs, root_inode))
     }
 }
 
-#[allow(dead_code)]
 #[derive(Clone)]
 enum Ext4Inode {
     File(Arc<FileInode>),
@@ -112,7 +164,7 @@ enum Ext4Inode {
 }
 
 impl Ext4Inode {
-    fn unwrap(self) -> Arc<dyn Inode> {
+    fn into_inner(self) -> Arc<dyn Inode> {
         match self {
             Ext4Inode::File(inode) => inode,
             Ext4Inode::Dir(inode) => inode,
@@ -124,96 +176,101 @@ 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
-    }
+define_struct_inode! {
+    struct DirInode;
 }
 
 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) {
+        match ext4fs.inner.read_at(self.ino as u32, offset, &mut temp_buf) {
             Ok(bytes_read) => {
-                buffer.fill(&temp_buf[..bytes_read]).map_err(|_| EIO)?;
-                Ok(bytes_read)
+                let _ = buffer.fill(&temp_buf[..bytes_read])?;
+                Ok(buffer.wrote())
             }
             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 name = String::from_utf8_lossy(&dentry.name());
+        let lookup_result = ext4fs.inner.fuse_lookup(self.ino, &name);
 
-                let inode = ext4fs.get_or_alloc_inode(new_ino, is_dir, size as u32);
-                Ok(Some(inode))
-            }
-            Err(e) => Err(e.error() as u32),
+        const EXT4_ERROR_ENOENT: Ext4Error = Ext4Error::new(Errno::ENOENT);
+        let attr = match lookup_result {
+            Ok(attr) => attr,
+            Err(EXT4_ERROR_ENOENT) => return Ok(None),
+            Err(error) => return Err(error.error() as u32),
+        };
+
+        // Fast path: if the inode is already in the cache, return it.
+        if let Some(inode) = ext4fs.try_get(&Task::block_on(ext4fs.icache.read()), attr.ino as u64)
+        {
+            return Ok(Some(inode));
         }
+
+        let extra_perm = attr.perm.bits() as u32 & 0o7000;
+        let perm = attr.perm.bits() as u32 & 0o0700;
+        let real_perm = extra_perm | perm | perm >> 3 | perm >> 6;
+
+        // Create a new inode based on the attributes.
+        let mut icache = Task::block_on(ext4fs.icache.write());
+        let inode = ext4fs.get_or_insert(
+            &mut icache,
+            InodeData {
+                ino: attr.ino as Ino,
+                size: AtomicU64::new(attr.size),
+                nlink: AtomicNlink::new(attr.nlink as _),
+                uid: AtomicU32::new(attr.uid),
+                gid: AtomicU32::new(attr.gid),
+                mode: AtomicU32::new(attr.kind.bits() as u32 | real_perm),
+                atime: Spin::new(TimeSpec {
+                    sec: attr.atime as _,
+                    nsec: 0,
+                }),
+                ctime: Spin::new(TimeSpec {
+                    sec: attr.ctime as _,
+                    nsec: 0,
+                }),
+                mtime: Spin::new(TimeSpec {
+                    sec: attr.mtime as _,
+                    nsec: 0,
+                }),
+                rwsem: RwLock::new(()),
+                vfs: self.vfs.clone(),
+            },
+        );
+
+        Ok(Some(inode))
     }
 
     fn do_readdir(
-            &self,
-            offset: usize,
-            callback: &mut dyn FnMut(&[u8], Ino) -> KResult<core::ops::ControlFlow<(), ()>>,
-        ) -> KResult<usize> {
+        &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 entries = ext4fs
+            .inner
+            .fuse_readdir(self.ino as u64, 0, offset as i64)
+            .map_err(|err| err.error() as u32)?;
         let mut current_offset = 0;
 
         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() {
+            if callback(name, entry.inode as Ino)?.is_break() {
                 break;
             }
 
@@ -226,10 +283,24 @@ impl Inode for DirInode {
 struct Ext4MountCreator;
 
 impl MountCreator for Ext4MountCreator {
-    fn create_mount(&self, _source: &str, _flags: u64, mp: &Arc<Dentry>) -> KResult<Mount> {
-        // TODO: temporarily the second disk, should generate from _source
-        let (ext4fs, root_inode) = 
-            Ext4Fs::create(make_device(8, 16))?;
+    fn check_signature(&self, mut first_block: &[u8]) -> KResult<bool> {
+        match first_block.split_off(1080..) {
+            Some([0x53, 0xef, ..]) => Ok(true), // Superblock signature
+            Some(..) => Ok(false),
+            None => Err(EIO),
+        }
+    }
+
+    fn create_mount(&self, source: &str, _flags: u64, mp: &Arc<Dentry>) -> KResult<Mount> {
+        let source = source.as_bytes();
+
+        let path = Path::new(source)?;
+        let device_dentry =
+            Dentry::open_recursive(&FsContext::global(), Dentry::root(), path, true, 0)?;
+        let devid = device_dentry.get_inode()?.devid()?;
+        let device = BlockDevice::get(devid)?;
+
+        let (ext4fs, root_inode) = Ext4Fs::create(device)?;
 
         Mount::new(mp, ext4fs, root_inode)
     }

+ 8 - 0
src/fs/fat32.rs

@@ -343,6 +343,14 @@ impl Inode for DirInode {
 struct FatMountCreator;
 
 impl MountCreator for FatMountCreator {
+    fn check_signature(&self, mut first_block: &[u8]) -> KResult<bool> {
+        match first_block.split_off(82..) {
+            Some([b'F', b'A', b'T', b'3', b'2', b' ', b' ', b' ', ..]) => Ok(true),
+            Some(..) => Ok(false),
+            None => Err(EIO),
+        }
+    }
+
     fn create_mount(&self, _source: &str, _flags: u64, mp: &Arc<Dentry>) -> KResult<Mount> {
         let (fatfs, root_inode) = FatFs::create(make_device(8, 1))?;
 

+ 4 - 0
src/fs/procfs.rs

@@ -202,6 +202,10 @@ impl MountCreator for ProcFsMountCreator {
         let root_inode = vfs.root_node.clone();
         Mount::new(mp, vfs, root_inode)
     }
+
+    fn check_signature(&self, _: &[u8]) -> KResult<bool> {
+        Ok(true)
+    }
 }
 
 pub fn root() -> ProcFsNode {

+ 4 - 0
src/fs/tmpfs.rs

@@ -383,6 +383,10 @@ impl MountCreator for TmpFsMountCreator {
 
         Mount::new(mp, fs, root_inode)
     }
+
+    fn check_signature(&self, _: &[u8]) -> KResult<bool> {
+        Ok(true)
+    }
 }
 
 pub fn init() {

+ 15 - 73
src/kernel/block.rs

@@ -5,10 +5,7 @@ use super::{
     mem::{paging::Page, AsMemoryBlock as _},
     vfs::DevId,
 };
-use crate::{
-    io::ByteBuffer,
-    kernel::constants::{EEXIST, EINVAL, EIO}
-};
+use crate::kernel::constants::{EEXIST, EINVAL};
 use crate::{
     io::{Buffer, FillResult},
     prelude::*,
@@ -57,6 +54,15 @@ pub enum FileSystemType {
     Fat32,
 }
 
+impl FileSystemType {
+    pub fn as_str(&self) -> &'static str {
+        match self {
+            FileSystemType::Ext4 => "ext4",
+            FileSystemType::Fat32 => "fat32",
+        }
+    }
+}
+
 pub struct BlockDevice {
     /// Unique device identifier, major and minor numbers
     devid: DevId,
@@ -123,14 +129,14 @@ impl BlockDevice {
         }
     }
 
-    pub fn register_partition(&self, idx: u32, offset: u64, size: u64) -> KResult<Arc<Self>> {
+    pub fn register_partition(&self, idx: usize, offset: u64, size: u64) -> KResult<Arc<Self>> {
         let queue = match &self.dev_type {
             BlockDeviceType::Disk { queue } => queue.clone(),
             BlockDeviceType::Partition { .. } => return Err(EINVAL),
         };
 
         let device = Arc::new(BlockDevice {
-            devid: make_device(self.devid >> 8, idx as u32),
+            devid: make_device(self.devid >> 8, (self.devid & 0xff) + idx as u32 + 1),
             sector_count: size,
             dev_type: BlockDeviceType::Partition {
                 disk_dev: self.devid,
@@ -145,81 +151,17 @@ impl BlockDevice {
         }
     }
 
-    fn is_ext4_signature(&self, buffer: &[u8]) -> bool {
-        if buffer.len() >= 1080 + 2 {
-            &buffer[1080..1082] == &[0x53, 0xEF]
-        } else {
-            false
-        }
-    }
-
-    fn is_fat32_signature(&self, buffer: &[u8]) -> bool {
-        if buffer.len() >= 90 {
-            &buffer[82..90] == b"FAT32   "
-        } else {
-            false
-        }
-    }
-
-    fn detect_filesystem(&self) -> KResult<FileSystemType> {
-        let mut buffer = vec![0u8; 4096];
-        let mut byte_buffer = ByteBuffer::new(buffer.as_mut_slice());
-        self.read_some(0, &mut byte_buffer)?.ok_or(EIO)?;
-
-        if self.is_ext4_signature(&buffer) {
-            Ok(FileSystemType::Ext4)
-        } else if self.is_fat32_signature(&buffer) {
-            Ok(FileSystemType::Fat32)
-        } else {
-            Err(0)
-        }
-    }
-
-    fn register_whole_disk_partition(&self) -> KResult<Arc<Self>> {
-        let queue = match &self.dev_type {
-            BlockDeviceType::Disk { queue } => queue.clone(),
-            BlockDeviceType::Partition { .. } => return Err(EINVAL),
-        };
-
-        let device = Arc::new(BlockDevice {
-            devid: make_device(self.devid >> 8, 0u32),
-            sector_count: self.sector_count,
-            dev_type: BlockDeviceType::Partition {
-                disk_dev: self.devid,
-                lba_offset: 0,
-                queue,
-            },
-        });
-
-        match BLOCK_DEVICE_LIST.lock().entry(device.devid()) {
-            Entry::Vacant(entry) => Ok(entry.insert(device).clone()),
-            Entry::Occupied(_) => Err(EEXIST),
-        }
-    }
-
     pub async fn partprobe(&self) -> KResult<()> {
         match self.dev_type {
             BlockDeviceType::Partition { .. } => Err(EINVAL),
             BlockDeviceType::Disk { .. } => {
-                let base_minor = (self.devid & 0xFF) as u32;
                 if let Ok(mbr_table) = MBRPartTable::from_disk(self).await {
                     for (idx, partition) in mbr_table.partitions().enumerate() {
-                        self.register_partition(
-                            base_minor + idx as u32 + 1,
-                            partition.lba_offset,
-                            partition.sector_count,
-                        )?;
-                    }
-                    Ok(())
-                } else {
-                    match self.detect_filesystem() {
-                        Ok(_fs_type) => {
-                            self.register_whole_disk_partition();
-                            Ok(())
-                        }
-                        Err(_) => Ok(()),
+                        self.register_partition(idx, partition.lba_offset, partition.sector_count)?;
                     }
                 }
+
+                Ok(())
             }
         }
     }

+ 1 - 0
src/kernel/vfs/mount.rs

@@ -54,6 +54,7 @@ unsafe impl Send for Mount {}
 unsafe impl Sync for Mount {}
 
 pub trait MountCreator: Send + Sync {
+    fn check_signature(&self, first_block: &[u8]) -> KResult<bool>;
     fn create_mount(&self, source: &str, flags: u64, mp: &Arc<Dentry>) -> KResult<Mount>;
 }