From a8ea4365cc82e0599ebbd31d950677598e777cf4 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Mon, 15 Dec 2025 21:46:56 -0800 Subject: [PATCH 1/7] working ext4 --- .gitignore | 2 + Cargo.lock | 30 ++- libkernel/Cargo.toml | 1 + libkernel/src/fs/attr.rs | 44 +++-- libkernel/src/fs/filesystems/ext4/mod.rs | 231 ++++++++++++++++++++++ libkernel/src/fs/filesystems/fat32/bpb.rs | 2 +- libkernel/src/fs/filesystems/mod.rs | 1 + scripts/qemu-runner.sh | 2 +- src/drivers/fs/ext4.rs | 43 ++++ src/drivers/fs/mod.rs | 3 + src/process/caps.rs | 4 +- 11 files changed, 338 insertions(+), 25 deletions(-) create mode 100644 libkernel/src/fs/filesystems/ext4/mod.rs create mode 100644 src/drivers/fs/ext4.rs diff --git a/.gitignore b/.gitignore index 45b53673..b097cbd1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ *.img .idea/ .vscode/ + +ext4_mount/ diff --git a/Cargo.lock b/Cargo.lock index 4cb9b9fc..799c9c1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -58,6 +58,21 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "crc" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -74,6 +89,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "ext4-view" +version = "0.9.3" +source = "git+https://github.com/arihant2math/ext4-view-rs.git?branch=main#2c4301fd24768d6e4c0cd1f890419b448b5160c7" +dependencies = [ + "async-trait", + "bitflags", + "crc", +] + [[package]] name = "fdt-parser" version = "0.4.18" @@ -192,6 +217,7 @@ version = "0.0.0" dependencies = [ "async-trait", "bitflags", + "ext4-view", "intrusive-collections", "log", "object", @@ -479,9 +505,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.111" +version = "2.0.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "21f182278bf2d2bcb3c88b1b08a37df029d71ce3d3ae26168e3c653b213b99d4" dependencies = [ "proc-macro2", "quote", diff --git a/libkernel/Cargo.toml b/libkernel/Cargo.toml index a36cd9de..409608b9 100644 --- a/libkernel/Cargo.toml +++ b/libkernel/Cargo.toml @@ -4,6 +4,7 @@ version = "0.0.0" edition = "2024" [dependencies] +ext4-view = { git = "https://github.com/arihant2math/ext4-view-rs.git", branch = "main" } paste = "1.0.15" thiserror = { version = "2.0.12", default-features = false } tock-registers = "0.10.1" diff --git a/libkernel/src/fs/attr.rs b/libkernel/src/fs/attr.rs index 2e90f368..a385fcf4 100644 --- a/libkernel/src/fs/attr.rs +++ b/libkernel/src/fs/attr.rs @@ -25,25 +25,31 @@ bitflags::bitflags! { bitflags! { #[derive(Clone, Copy, Debug)] pub struct FilePermissions: u16 { - // Owner permissions - const S_IRUSR = 0o400; // Read permission, owner - const S_IWUSR = 0o200; // Write permission, owner - const S_IXUSR = 0o100; // Execute/search permission, owner - - // Group permissions - const S_IRGRP = 0o040; // Read permission, group - const S_IWGRP = 0o020; // Write permission, group - const S_IXGRP = 0o010; // Execute/search permission, group - - // Others permissions - const S_IROTH = 0o004; // Read permission, others - const S_IWOTH = 0o002; // Write permission, others - const S_IXOTH = 0o001; // Execute/search permission, others - - // Optional: sticky/setuid/setgid bits - const S_ISUID = 0o4000; // Set-user-ID on execution - const S_ISGID = 0o2000; // Set-group-ID on execution - const S_ISVTX = 0o1000; // Sticky bit + const S_IXOTH = 0x0001; + const S_IWOTH = 0x0002; + const S_IROTH = 0x0004; + + const S_IXGRP = 0x0008; + const S_IWGRP = 0x0010; + const S_IRGRP = 0x0020; + + const S_IXUSR = 0x0040; + const S_IWUSR = 0x0080; + const S_IRUSR = 0x0100; + + const S_ISVTX = 0x0200; + + const S_ISGID = 0x0400; + const S_ISUID = 0x0800; + + // Mutually-exclusive file types: + const S_IFIFO = 0x1000; + const S_IFCHR = 0x2000; + const S_IFDIR = 0x4000; + const S_IFBLK = 0x6000; + const S_IFREG = 0x8000; + const S_IFLNK = 0xA000; + const S_IFSOCK = 0xC000; } } diff --git a/libkernel/src/fs/filesystems/ext4/mod.rs b/libkernel/src/fs/filesystems/ext4/mod.rs new file mode 100644 index 00000000..6fc1a1e3 --- /dev/null +++ b/libkernel/src/fs/filesystems/ext4/mod.rs @@ -0,0 +1,231 @@ +//! EXT4 Filesystem Driver + +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused_imports)] + +use crate::error::FsError; +use crate::fs::pathbuf::PathBuf; +use crate::fs::{DirStream, Dirent}; +use crate::proc::ids::{Gid, Uid}; +use crate::{ + error::{KernelError, Result}, + fs::{ + FileType, Filesystem, Inode, InodeId, + attr::{FileAttr, FilePermissions}, + blk::buffer::BlockBuffer, + }, +}; +use alloc::string::ToString; +use alloc::{ + boxed::Box, + sync::{Arc, Weak}, +}; +use async_trait::async_trait; +use core::error::Error; +use ext4_view::{AsyncIterator, AsyncSkip, Ext4, Ext4Read, FollowSymlinks, Metadata, ReadDir}; + +#[async_trait] +impl Ext4Read for BlockBuffer { + async fn read( + &self, + start_byte: u64, + dst: &mut [u8], + ) -> core::result::Result<(), Box> { + Ok(self.read_at(start_byte, dst).await?) + } +} + +impl From for KernelError { + fn from(err: ext4_view::Ext4Error) -> Self { + match err { + ext4_view::Ext4Error::NotFound => KernelError::Fs(FsError::NotFound), + ext4_view::Ext4Error::NotADirectory => KernelError::Fs(FsError::NotADirectory), + _ => KernelError::Fs(FsError::InvalidFs), + } + } +} + +impl From for FileType { + fn from(ft: ext4_view::FileType) -> Self { + match ft { + ext4_view::FileType::BlockDevice => todo!(), + ext4_view::FileType::CharacterDevice => todo!(), + ext4_view::FileType::Directory => FileType::Directory, + ext4_view::FileType::Fifo => FileType::Fifo, + ext4_view::FileType::Regular => FileType::File, + ext4_view::FileType::Socket => FileType::Socket, + ext4_view::FileType::Symlink => FileType::Symlink, + } + } +} + +impl From for FileAttr { + fn from(meta: Metadata) -> Self { + FileAttr { + size: meta.size_in_bytes, + file_type: meta.file_type.into(), + mode: FilePermissions::from_bits(meta.mode.bits()).unwrap(), + uid: Uid::new(meta.uid), + gid: Gid::new(meta.gid), + ..Default::default() + } + } +} + +pub struct ReadDirWrapper { + inner: AsyncSkip, + fs_id: u64, +} + +impl ReadDirWrapper { + pub fn new(inner: ReadDir, fs_id: u64, start_offset: u64) -> Self { + Self { + inner: inner.skip(start_offset as usize), + fs_id, + } + } +} + +#[async_trait] +impl DirStream for ReadDirWrapper { + async fn next_entry(&mut self) -> Result> { + match self.inner.next().await { + Some(entry) => { + let entry = entry?; + Ok(Some(Dirent { + id: InodeId::from_fsid_and_inodeid(self.fs_id, entry.inode.get() as u64), + name: entry.file_name().as_str().unwrap().to_string(), + file_type: entry.file_type()?.into(), + offset: 0, + })) + } + None => Ok(None), + } + } +} + +pub struct Ext4Inode { + fs_ref: Weak, + inner: ext4_view::Inode, + path: ext4_view::PathBuf, +} + +#[async_trait] +impl Inode for Ext4Inode { + fn id(&self) -> InodeId { + let fs = self.fs_ref.upgrade().unwrap(); + InodeId::from_fsid_and_inodeid(fs.id(), self.inner.index.get() as u64) + } + + async fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result { + if self.inner.metadata.file_type != ext4_view::FileType::Regular { + return Err(KernelError::NotSupported); + } + let fs = self.fs_ref.upgrade().unwrap(); + // TODO: Optimize to read directly into buf instead of allocating a new Vec + let vec = fs.inner.read_inode_file(&self.inner).await?; + let file_size = vec.len() as u64; + if offset >= file_size { + return Ok(0); + } + let to_read = core::cmp::min(buf.len() as u64, file_size - offset) as usize; + buf[..to_read].copy_from_slice(&vec[offset as usize..(offset as usize + to_read)]); + Ok(to_read) + } + + async fn write_at(&self, _offset: u64, _buf: &[u8]) -> Result { + Err(KernelError::NotSupported) + } + + async fn truncate(&self, _size: u64) -> Result<()> { + Err(KernelError::NotSupported) + } + + async fn getattr(&self) -> Result { + Ok(self.inner.metadata.clone().into()) + } + + async fn lookup(&self, name: &str) -> Result> { + let fs = self.fs_ref.upgrade().unwrap(); + let child_path = self.path.join(name); + let child_inode = fs + .inner + .path_to_inode(child_path.as_path(), FollowSymlinks::All) + .await?; + Ok(Arc::new(Ext4Inode { + fs_ref: self.fs_ref.clone(), + inner: child_inode, + path: child_path, + })) + } + + async fn create( + &self, + _name: &str, + _file_type: FileType, + _permissions: FilePermissions, + ) -> Result> { + Err(KernelError::NotSupported) + } + + async fn unlink(&self, _name: &str) -> Result<()> { + Err(KernelError::NotSupported) + } + + async fn readdir(&self, start_offset: u64) -> Result> { + if self.inner.metadata.file_type != ext4_view::FileType::Directory { + return Err(KernelError::NotSupported); + } + let fs = self.fs_ref.upgrade().unwrap(); + Ok(Box::new(ReadDirWrapper::new( + ReadDir::new(fs.inner.clone(), &self.inner, self.path.clone())?, + fs.id(), + start_offset, + ))) + } +} + +/// An EXT4 filesystem instance. +/// +/// For now this struct only stores the underlying block buffer and an ID +/// assigned by the VFS when the filesystem is mounted. +pub struct Ext4Filesystem { + inner: Ext4, + id: u64, + this: Weak, +} + +impl Ext4Filesystem { + /// Construct a new EXT4 filesystem instance. + pub async fn new(dev: BlockBuffer, id: u64) -> Result> { + let inner = Ext4::load(Box::new(dev)).await?; + Ok(Arc::new_cyclic(|weak| Self { + inner, + id, + this: weak.clone(), + })) + } +} + +#[async_trait] +impl Filesystem for Ext4Filesystem { + fn id(&self) -> u64 { + self.id + } + + /// Returns the root inode of the mounted EXT4 filesystem. + async fn root_inode(&self) -> Result> { + Ok(Arc::new(Ext4Inode { + fs_ref: self.this.clone(), + inner: self.inner.read_root_inode().await?, + path: ext4_view::PathBuf::new("/"), + })) + } + + /// Flushes any dirty data to the underlying block device. The current + /// stub implementation simply forwards the request to `BlockBuffer::sync`. + async fn sync(&self) -> Result<()> { + Ok(()) + } +} diff --git a/libkernel/src/fs/filesystems/fat32/bpb.rs b/libkernel/src/fs/filesystems/fat32/bpb.rs index d9b6f608..b0420aee 100644 --- a/libkernel/src/fs/filesystems/fat32/bpb.rs +++ b/libkernel/src/fs/filesystems/fat32/bpb.rs @@ -106,7 +106,7 @@ impl BiosParameterBlock { } pub fn fat_region(&self, fat_number: usize) -> Option<(Sector, Sector)> { - if fat_number >= self.num_fats as _ { + if fat_number >= self.num_fats as usize { None } else { let start = self.fat_region_start() + self.fat_len() * fat_number; diff --git a/libkernel/src/fs/filesystems/mod.rs b/libkernel/src/fs/filesystems/mod.rs index c50261a8..4b763b6a 100644 --- a/libkernel/src/fs/filesystems/mod.rs +++ b/libkernel/src/fs/filesystems/mod.rs @@ -1,2 +1,3 @@ +pub mod ext4; pub mod fat32; pub mod tmpfs; diff --git a/scripts/qemu-runner.sh b/scripts/qemu-runner.sh index 4a650fe6..bfb1a9db 100755 --- a/scripts/qemu-runner.sh +++ b/scripts/qemu-runner.sh @@ -23,4 +23,4 @@ bin="${elf%.elf}.bin" # Convert to binary format aarch64-none-elf-objcopy -O binary "$elf" "$bin" -qemu-system-aarch64 -M virt,gic-version=3 -initrd moss.img -cpu cortex-a72 -m 2G -smp 4 -nographic -s -kernel "$bin" -append "--init=$init_script --rootfs=fat32fs --automount=/dev,devfs --automount=/tmp,tmpfs --automount=/proc,procfs" +qemu-system-aarch64 -M virt,gic-version=3 -initrd moss.img -cpu cortex-a72 -m 2G -smp 4 -nographic -s -kernel "$bin" -append "--init=$init_script --rootfs=ext4fs --automount=/dev,devfs --automount=/tmp,tmpfs --automount=/proc,procfs" diff --git a/src/drivers/fs/ext4.rs b/src/drivers/fs/ext4.rs new file mode 100644 index 00000000..2bb4a877 --- /dev/null +++ b/src/drivers/fs/ext4.rs @@ -0,0 +1,43 @@ +use crate::{drivers::Driver, fs::FilesystemDriver}; +use alloc::{boxed::Box, sync::Arc}; +use async_trait::async_trait; +use libkernel::{ + error::{KernelError, Result}, + fs::{BlockDevice, Filesystem, blk::buffer::BlockBuffer, filesystems::ext4::Ext4Filesystem}, +}; +use log::warn; + +pub struct Ext4FsDriver {} + +impl Ext4FsDriver { + pub fn new() -> Self { + Self {} + } +} + +impl Driver for Ext4FsDriver { + fn name(&self) -> &'static str { + "ext4fs" + } + + fn as_filesystem_driver(self: Arc) -> Option> { + Some(self) + } +} + +#[async_trait] +impl FilesystemDriver for Ext4FsDriver { + async fn construct( + &self, + fs_id: u64, + device: Option>, + ) -> Result> { + match device { + Some(dev) => Ok(Ext4Filesystem::new(BlockBuffer::new(dev), fs_id).await?), + None => { + warn!("Could not mount fat32 fs with no block device"); + Err(KernelError::InvalidValue) + } + } + } +} diff --git a/src/drivers/fs/mod.rs b/src/drivers/fs/mod.rs index a5688b66..89399462 100644 --- a/src/drivers/fs/mod.rs +++ b/src/drivers/fs/mod.rs @@ -1,5 +1,6 @@ use alloc::sync::Arc; use dev::DevFsDriver; +use ext4::Ext4FsDriver; use fat32::Fat32FsDriver; use proc::ProcFsDriver; use tmpfs::TmpFsDriver; @@ -7,6 +8,7 @@ use tmpfs::TmpFsDriver; use super::DM; pub mod dev; +pub mod ext4; pub mod fat32; pub mod proc; pub mod tmpfs; @@ -14,6 +16,7 @@ pub mod tmpfs; pub fn register_fs_drivers() { let mut dm = DM.lock_save_irq(); + dm.insert_driver(Arc::new(Ext4FsDriver::new())); dm.insert_driver(Arc::new(Fat32FsDriver::new())); dm.insert_driver(Arc::new(DevFsDriver::new())); dm.insert_driver(Arc::new(ProcFsDriver::new())); diff --git a/src/process/caps.rs b/src/process/caps.rs index 893656a8..5c807d11 100644 --- a/src/process/caps.rs +++ b/src/process/caps.rs @@ -58,7 +58,7 @@ pub async fn sys_capget(hdrp: TUA, datap: TUA) -> Re TASK_LIST .lock_save_irq() .iter() - .find(|task| task.0.tgid.value() == header.pid as _) + .find(|task| task.0.tgid.value() == header.pid as u32) .and_then(|task| task.1.upgrade()) .ok_or(KernelError::NoProcess)? }; @@ -93,7 +93,7 @@ pub async fn sys_capset(hdrp: TUA, datap: TUA) -> Re TASK_LIST .lock_save_irq() .iter() - .find(|task| task.0.tgid.value() == header.pid as _) + .find(|task| task.0.tgid.value() == header.pid as u32) .and_then(|task| task.1.upgrade()) .ok_or(KernelError::NoProcess)? }; From 43d678967057c2adb55d157fac62b1e8227d0bf1 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Tue, 30 Dec 2025 23:28:54 -0800 Subject: [PATCH 2/7] optimize read_at --- libkernel/src/fs/filesystems/ext4/mod.rs | 36 +++++++++++++++++++----- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/libkernel/src/fs/filesystems/ext4/mod.rs b/libkernel/src/fs/filesystems/ext4/mod.rs index 6fc1a1e3..68d4ae13 100644 --- a/libkernel/src/fs/filesystems/ext4/mod.rs +++ b/libkernel/src/fs/filesystems/ext4/mod.rs @@ -23,7 +23,9 @@ use alloc::{ }; use async_trait::async_trait; use core::error::Error; -use ext4_view::{AsyncIterator, AsyncSkip, Ext4, Ext4Read, FollowSymlinks, Metadata, ReadDir}; +use ext4_view::{ + AsyncIterator, AsyncSkip, Ext4, Ext4Read, File, FollowSymlinks, Metadata, ReadDir, +}; #[async_trait] impl Ext4Read for BlockBuffer { @@ -119,19 +121,39 @@ impl Inode for Ext4Inode { } async fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result { + // Must be a regular file. if self.inner.metadata.file_type != ext4_view::FileType::Regular { return Err(KernelError::NotSupported); } - let fs = self.fs_ref.upgrade().unwrap(); - // TODO: Optimize to read directly into buf instead of allocating a new Vec - let vec = fs.inner.read_inode_file(&self.inner).await?; - let file_size = vec.len() as u64; + + let file_size = self.inner.metadata.size_in_bytes; + + // Past EOF = nothing to read. if offset >= file_size { return Ok(0); } + + // Do not read past the end of the file. let to_read = core::cmp::min(buf.len() as u64, file_size - offset) as usize; - buf[..to_read].copy_from_slice(&vec[offset as usize..(offset as usize + to_read)]); - Ok(to_read) + + let fs = self.fs_ref.upgrade().unwrap(); + let mut file = File::open_inode(&fs.inner, self.inner.clone())?; + + file.seek_to(offset).await?; + + // `ext4_view::File::read_bytes` may return fewer bytes than requested + // if the read crosses a block boundary. Loop until we've filled + // `to_read` bytes or hit EOF. + let mut total_read = 0; + while total_read < to_read { + let bytes_read = file.read_bytes(&mut buf[total_read..to_read]).await?; + if bytes_read == 0 { + break; // EOF + } + total_read += bytes_read; + } + + Ok(total_read) } async fn write_at(&self, _offset: u64, _buf: &[u8]) -> Result { From cf25bff724bb4f2a166a6b66de1a5f505f90c0e3 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Tue, 30 Dec 2025 23:59:29 -0800 Subject: [PATCH 3/7] add note about lookup being suboptimal --- libkernel/src/fs/filesystems/ext4/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libkernel/src/fs/filesystems/ext4/mod.rs b/libkernel/src/fs/filesystems/ext4/mod.rs index 68d4ae13..8b6c1ac7 100644 --- a/libkernel/src/fs/filesystems/ext4/mod.rs +++ b/libkernel/src/fs/filesystems/ext4/mod.rs @@ -169,6 +169,7 @@ impl Inode for Ext4Inode { } async fn lookup(&self, name: &str) -> Result> { + // TODO: use get_dir_entry_inode_by_name, which is more efficient. let fs = self.fs_ref.upgrade().unwrap(); let child_path = self.path.join(name); let child_inode = fs From 2704e455f75d5290f1d90f24000e569f3e2c90a6 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Wed, 31 Dec 2025 00:02:41 -0800 Subject: [PATCH 4/7] fix readdir --- Cargo.lock | 2 +- libkernel/src/fs/filesystems/ext4/mod.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 799c9c1e..a64862ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,7 +92,7 @@ dependencies = [ [[package]] name = "ext4-view" version = "0.9.3" -source = "git+https://github.com/arihant2math/ext4-view-rs.git?branch=main#2c4301fd24768d6e4c0cd1f890419b448b5160c7" +source = "git+https://github.com/arihant2math/ext4-view-rs.git?branch=main#e63bccb01b2684df1dede05b7ac3330274d54d3a" dependencies = [ "async-trait", "bitflags", diff --git a/libkernel/src/fs/filesystems/ext4/mod.rs b/libkernel/src/fs/filesystems/ext4/mod.rs index 8b6c1ac7..a47d2db7 100644 --- a/libkernel/src/fs/filesystems/ext4/mod.rs +++ b/libkernel/src/fs/filesystems/ext4/mod.rs @@ -78,6 +78,7 @@ impl From for FileAttr { pub struct ReadDirWrapper { inner: AsyncSkip, fs_id: u64, + current_off: u64, } impl ReadDirWrapper { @@ -85,6 +86,7 @@ impl ReadDirWrapper { Self { inner: inner.skip(start_offset as usize), fs_id, + current_off: start_offset, } } } @@ -95,11 +97,12 @@ impl DirStream for ReadDirWrapper { match self.inner.next().await { Some(entry) => { let entry = entry?; + self.current_off += 1; Ok(Some(Dirent { id: InodeId::from_fsid_and_inodeid(self.fs_id, entry.inode.get() as u64), name: entry.file_name().as_str().unwrap().to_string(), file_type: entry.file_type()?.into(), - offset: 0, + offset: self.current_off, })) } None => Ok(None), From 70df1a945bebecd8067d40d68fc6f3d497f713fb Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Wed, 31 Dec 2025 08:39:56 -0800 Subject: [PATCH 5/7] switch qemu-runner back --- scripts/qemu-runner.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/qemu-runner.sh b/scripts/qemu-runner.sh index bfb1a9db..4a650fe6 100755 --- a/scripts/qemu-runner.sh +++ b/scripts/qemu-runner.sh @@ -23,4 +23,4 @@ bin="${elf%.elf}.bin" # Convert to binary format aarch64-none-elf-objcopy -O binary "$elf" "$bin" -qemu-system-aarch64 -M virt,gic-version=3 -initrd moss.img -cpu cortex-a72 -m 2G -smp 4 -nographic -s -kernel "$bin" -append "--init=$init_script --rootfs=ext4fs --automount=/dev,devfs --automount=/tmp,tmpfs --automount=/proc,procfs" +qemu-system-aarch64 -M virt,gic-version=3 -initrd moss.img -cpu cortex-a72 -m 2G -smp 4 -nographic -s -kernel "$bin" -append "--init=$init_script --rootfs=fat32fs --automount=/dev,devfs --automount=/tmp,tmpfs --automount=/proc,procfs" From 4d9121b84b9a7cd6dd77e3ffaf13fc1e3b091374 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Wed, 31 Dec 2025 12:06:40 -0800 Subject: [PATCH 6/7] support readlink --- libkernel/src/fs/filesystems/ext4/mod.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/libkernel/src/fs/filesystems/ext4/mod.rs b/libkernel/src/fs/filesystems/ext4/mod.rs index a47d2db7..20384d7f 100644 --- a/libkernel/src/fs/filesystems/ext4/mod.rs +++ b/libkernel/src/fs/filesystems/ext4/mod.rs @@ -43,6 +43,7 @@ impl From for KernelError { match err { ext4_view::Ext4Error::NotFound => KernelError::Fs(FsError::NotFound), ext4_view::Ext4Error::NotADirectory => KernelError::Fs(FsError::NotADirectory), + ext4_view::Ext4Error::Corrupt(_) => KernelError::Fs(FsError::InvalidFs), _ => KernelError::Fs(FsError::InvalidFs), } } @@ -67,6 +68,7 @@ impl From for FileAttr { FileAttr { size: meta.size_in_bytes, file_type: meta.file_type.into(), + // Infallible, since they are identical mode: FilePermissions::from_bits(meta.mode.bits()).unwrap(), uid: Uid::new(meta.uid), gid: Gid::new(meta.gid), @@ -210,6 +212,15 @@ impl Inode for Ext4Inode { start_offset, ))) } + + async fn readlink(&self) -> Result { + if self.inner.metadata.file_type != ext4_view::FileType::Symlink { + return Err(KernelError::NotSupported); + } + let fs = self.fs_ref.upgrade().unwrap(); + // Conversion has to ensure path is valid UTF-8 (O(n) time). + Ok(self.inner.symlink_target(&fs.inner).await.map(|p| PathBuf::from(p.to_str().unwrap()))?) + } } /// An EXT4 filesystem instance. From e43ff6e208bc62c746081d21e6a32abdc978ef81 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Wed, 31 Dec 2025 12:11:52 -0800 Subject: [PATCH 7/7] optimize lookup --- Cargo.lock | 2 +- libkernel/src/fs/filesystems/ext4/mod.rs | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a64862ab..0c5eacdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,7 +92,7 @@ dependencies = [ [[package]] name = "ext4-view" version = "0.9.3" -source = "git+https://github.com/arihant2math/ext4-view-rs.git?branch=main#e63bccb01b2684df1dede05b7ac3330274d54d3a" +source = "git+https://github.com/arihant2math/ext4-view-rs.git?branch=main#c12b2acf43c7472096752b509af0fd92b0731e8c" dependencies = [ "async-trait", "bitflags", diff --git a/libkernel/src/fs/filesystems/ext4/mod.rs b/libkernel/src/fs/filesystems/ext4/mod.rs index 20384d7f..b1abe1f7 100644 --- a/libkernel/src/fs/filesystems/ext4/mod.rs +++ b/libkernel/src/fs/filesystems/ext4/mod.rs @@ -25,6 +25,7 @@ use async_trait::async_trait; use core::error::Error; use ext4_view::{ AsyncIterator, AsyncSkip, Ext4, Ext4Read, File, FollowSymlinks, Metadata, ReadDir, + get_dir_entry_inode_by_name, }; #[async_trait] @@ -174,13 +175,15 @@ impl Inode for Ext4Inode { } async fn lookup(&self, name: &str) -> Result> { - // TODO: use get_dir_entry_inode_by_name, which is more efficient. let fs = self.fs_ref.upgrade().unwrap(); + let child_inode = get_dir_entry_inode_by_name( + &fs.inner, + &self.inner, + ext4_view::DirEntryName::try_from(name) + .map_err(|_| KernelError::Fs(FsError::InvalidInput))?, + ) + .await?; let child_path = self.path.join(name); - let child_inode = fs - .inner - .path_to_inode(child_path.as_path(), FollowSymlinks::All) - .await?; Ok(Arc::new(Ext4Inode { fs_ref: self.fs_ref.clone(), inner: child_inode, @@ -219,7 +222,11 @@ impl Inode for Ext4Inode { } let fs = self.fs_ref.upgrade().unwrap(); // Conversion has to ensure path is valid UTF-8 (O(n) time). - Ok(self.inner.symlink_target(&fs.inner).await.map(|p| PathBuf::from(p.to_str().unwrap()))?) + Ok(self + .inner + .symlink_target(&fs.inner) + .await + .map(|p| PathBuf::from(p.to_str().unwrap()))?) } }