From 88fab7c063eec54b00cf200ad84fb4ab2b3ece45 Mon Sep 17 00:00:00 2001 From: yuoo655 Date: Sat, 1 Jun 2024 21:03:35 +0800 Subject: [PATCH 1/3] add ext4_rs --- Cargo.lock | 9 + api/axfeat/Cargo.toml | 1 + modules/axfs/Cargo.toml | 2 + modules/axfs/src/dev.rs | 17 ++ modules/axfs/src/fs/ext4.rs | 424 ++++++++++++++++++++++++++++++++++++ modules/axfs/src/fs/mod.rs | 3 + modules/axfs/src/root.rs | 4 + scripts/make/features.mk | 3 + ulib/axstarry/Cargo.toml | 1 + 9 files changed, 464 insertions(+) create mode 100644 modules/axfs/src/fs/ext4.rs diff --git a/Cargo.lock b/Cargo.lock index 3fc63b10c0..733a97e0da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -337,6 +337,7 @@ dependencies = [ "cfg-if", "crate_interface", "driver_block", + "ext4_rs", "fatfs", "lazy_init", "log", @@ -1133,6 +1134,14 @@ dependencies = [ "bitflags 2.4.0", ] +[[package]] +name = "ext4_rs" +version = "1.0.0" +dependencies = [ + "bitflags 2.4.0", + "log", +] + [[package]] name = "fatfs" version = "0.4.0" diff --git a/api/axfeat/Cargo.toml b/api/axfeat/Cargo.toml index aa5d39099f..75c3d86d60 100644 --- a/api/axfeat/Cargo.toml +++ b/api/axfeat/Cargo.toml @@ -49,6 +49,7 @@ fs = ["alloc", "paging", "axdriver/virtio-blk", "dep:axfs", "axruntime/fs"] # TO fatfs = ["axfs/fatfs"] ext4fs = ["axfs/ext4fs"] myfs = ["axfs?/myfs"] +ext4_rs = ["axfs/ext4_rs"] # Networking net = ["alloc", "paging", "axdriver/virtio-net", "dep:axnet", "axruntime/net"] diff --git a/modules/axfs/Cargo.toml b/modules/axfs/Cargo.toml index 7f4e533319..4164269ba2 100644 --- a/modules/axfs/Cargo.toml +++ b/modules/axfs/Cargo.toml @@ -18,6 +18,7 @@ myfs = ["dep:crate_interface"] use-ramdisk = [] monolithic = [] fatfs = ["dep:fatfs"] +ext4_rs = ["dep:ext4_rs", "devfs", "ramfs", "procfs", "sysfs"] ext4fs = ["dep:lwext4_rust", "devfs", "ramfs", "procfs", "sysfs",] default = ["devfs", "ramfs", "fatfs", "procfs", "sysfs"] @@ -33,6 +34,7 @@ axconfig = { path = "../axconfig", optional = true } axfs_vfs = { path = "../../crates/axfs_vfs" } axfs_devfs = { path = "../../crates/axfs_devfs", optional = true } axfs_ramfs = { path = "../../crates/axfs_ramfs", optional = true } +ext4_rs = { path = "https://github.com/yuoo655/ext4_rs.git", optional = true } lwext4_rust = { git = "https://github.com/elliott10/lwext4_rust.git", rev = "f3048f87", optional = true } axdriver = { path = "../axdriver", features = ["block"] } axsync = { path = "../axsync" } diff --git a/modules/axfs/src/dev.rs b/modules/axfs/src/dev.rs index c28eef6999..6ca54d6477 100644 --- a/modules/axfs/src/dev.rs +++ b/modules/axfs/src/dev.rs @@ -93,4 +93,21 @@ impl Disk { }; Ok(write_size) } + + /// Read a single block starting from the specified offset. + pub fn read_offset(&mut self, offset: usize) -> [u8; BLOCK_SIZE] { + let block_id = offset / BLOCK_SIZE; + let mut block_data = [0u8; BLOCK_SIZE]; + self.dev.read_block(block_id as u64, &mut block_data).unwrap(); + block_data + } + + /// Write single block starting from the specified offset. + pub fn write_offset(&mut self, offset: usize, buf: &[u8]) -> DevResult { + assert!(buf.len() == BLOCK_SIZE, "Buffer length must be equal to BLOCK_SIZE"); + assert!(offset % BLOCK_SIZE == 0); + let block_id = offset / BLOCK_SIZE; + self.dev.write_block(block_id as u64, buf).unwrap(); + Ok(buf.len()) + } } diff --git a/modules/axfs/src/fs/ext4.rs b/modules/axfs/src/fs/ext4.rs new file mode 100644 index 0000000000..913d49af37 --- /dev/null +++ b/modules/axfs/src/fs/ext4.rs @@ -0,0 +1,424 @@ +use crate::alloc::string::String; +use crate::dev::Disk; +use alloc::sync::Arc; +use alloc::vec; +use alloc::vec::*; +use axerrno::AxError; +use axfs_vfs::{VfsDirEntry, VfsError, VfsNodePerm, VfsResult}; +use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps}; +use axsync::Mutex; +use core::cell::RefCell; +use core::fmt::write; +use ext4_rs::*; + +pub struct DiskAdapter { + inner: RefCell, +} + +unsafe impl Send for DiskAdapter {} +unsafe impl Sync for DiskAdapter {} + +const DISK_BLOCK_SIZE: usize = 512; +const BLOCK_SIZE: usize = 4096; + +impl BlockDevice for DiskAdapter { + fn read_offset(&self, offset: usize) -> Vec { + let mut disk = self.inner.borrow_mut(); + let mut buf = vec![0u8; BLOCK_SIZE]; + + let start_block_id = offset / DISK_BLOCK_SIZE; + let mut offset_in_block = offset % DISK_BLOCK_SIZE; + let mut total_bytes_read = 0; + + while total_bytes_read < buf.len() { + let current_block_id = start_block_id + (total_bytes_read / DISK_BLOCK_SIZE); + let bytes_to_copy = + (buf.len() - total_bytes_read).min(DISK_BLOCK_SIZE - offset_in_block); + + let block_data = disk.read_offset(current_block_id * DISK_BLOCK_SIZE + offset_in_block); + + buf[total_bytes_read..total_bytes_read + bytes_to_copy] + .copy_from_slice(&block_data[offset_in_block..offset_in_block + bytes_to_copy]); + + total_bytes_read += bytes_to_copy; + offset_in_block = 0; // After the first block, subsequent blocks read from the beginning + } + + buf + } + + fn write_offset(&self, offset: usize, buf: &[u8]) { + let mut disk = self.inner.borrow_mut(); + + let start_block_id = offset / DISK_BLOCK_SIZE; + let mut offset_in_block = offset % DISK_BLOCK_SIZE; + + let bytes_to_write = buf.len(); + let mut total_bytes_written = 0; + + while total_bytes_written < bytes_to_write { + let current_block_id = start_block_id + (total_bytes_written / DISK_BLOCK_SIZE); + let bytes_to_copy = + (bytes_to_write - total_bytes_written).min(DISK_BLOCK_SIZE - offset_in_block); + + let mut block_data = disk.read_offset(current_block_id * DISK_BLOCK_SIZE); + + block_data[offset_in_block..offset_in_block + bytes_to_copy] + .copy_from_slice(&buf[total_bytes_written..total_bytes_written + bytes_to_copy]); + + disk.write_offset(current_block_id * DISK_BLOCK_SIZE, &block_data) + .unwrap(); + + total_bytes_written += bytes_to_copy; + offset_in_block = 0; // After the first block, subsequent blocks start at the beginning + } + } +} + +pub struct Ext4FileSystem { + inner: Arc, + root_dir: VfsNodeRef, +} + +impl Ext4FileSystem { + pub fn new(disk: Disk) -> Self { + let block_device = Arc::new(DiskAdapter { + inner: RefCell::new(disk), + }); + let inner = Ext4::open(block_device); + let root = Arc::new(Ext4FileWrapper::new(inner.clone())); + Self { + inner: inner.clone(), + root_dir: root, + } + } +} + +impl VfsOps for Ext4FileSystem { + fn root_dir(&self) -> VfsNodeRef { + Arc::clone(&self.root_dir) + } + + fn umount(&self) -> VfsResult { + log::info!("umount:"); + todo!() + } +} + +pub struct Ext4FileWrapper { + ext4_file: Mutex, + ext4: Arc, +} + +unsafe impl Send for Ext4FileWrapper {} +unsafe impl Sync for Ext4FileWrapper {} + +impl Ext4FileWrapper { + fn new(ext4: Arc) -> Self { + Self { + ext4_file: Mutex::new(Ext4File::new()), + ext4: ext4, + } + } +} + +impl VfsNodeOps for Ext4FileWrapper { + /// Do something when the node is opened. + fn open(&self) -> VfsResult { + // log::info!("opening file"); + // let mut ext4_file = self.ext4_file.lock(); + // let r = self.ext4.ext4_open(&mut ext4_file, path, "r+", false); + Ok(()) + } + + /// Do something when the node is closed. + fn release(&self) -> VfsResult { + Ok(()) + } + + /// Get the attributes of the node. + fn get_attr(&self) -> VfsResult { + let ext4_file = self.ext4_file.lock(); + let mut root_inode_ref = + Ext4InodeRef::get_inode_ref(Arc::downgrade(&self.ext4).clone(), ext4_file.inode); + let inode_mode = root_inode_ref.inner.inode.mode; + let size = ext4_file.fsize; + // BLOCK_SIZE / DISK_BLOCK_SIZE + let blocks = root_inode_ref.inner.inode.blocks_lo * 8; + let (ty, perm) = map_imode(inode_mode as u16); + drop(ext4_file); + Ok(VfsNodeAttr::new(perm, ty, size as _, blocks as _)) + } + + // file operations: + + /// Read data from the file at the given offset. + fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult { + let mut ext4_file = self.ext4_file.lock(); + ext4_file.fpos = offset as usize; + + let read_len = buf.len(); + let mut read_cnt = 0; + + let r = self + .ext4 + .ext4_file_read(&mut ext4_file, buf, read_len, &mut read_cnt); + + if let Err(e) = r { + match e.error() { + Errnum::EINVAL => { + drop(ext4_file); + Ok(0) + } + _ => { + drop(ext4_file); + Err(VfsError::InvalidInput) + } + } + } else { + drop(ext4_file); + Ok(read_len) + } + } + + /// Write data to the file at the given offset. + fn write_at(&self, offset: u64, buf: &[u8]) -> VfsResult { + let mut ext4_file = self.ext4_file.lock(); + ext4_file.fpos = offset as usize; + + let write_size = buf.len(); + + self.ext4.ext4_file_write(&mut ext4_file, &buf, write_size); + + Ok(write_size) + } + + /// Flush the file, synchronize the data to disk. + fn fsync(&self) -> VfsResult { + todo!() + } + + /// Truncate the file to the given size. + fn truncate(&self, _size: u64) -> VfsResult { + todo!() + } + + // directory operations: + + /// Get the parent directory of this directory. + /// + /// Return `None` if the node is a file. + fn parent(&self) -> Option { + None + } + + /// Lookup the node with given `path` in the directory. + /// + /// Return the node if found. + fn lookup(self: Arc, path: &str) -> VfsResult { + if path == "" { + log::error!("open root"); + } + let mut ext4_file = self.ext4_file.lock(); + let r = self.ext4.ext4_open(&mut ext4_file, path, "r+", false); + + // log::error!("lookup path {:?} file size {:x?}", path, ext4_file.fsize); + + if let Err(e) = r { + match e.error() { + Errnum::ENOENT => Err(VfsError::NotFound), + Errnum::EALLOCFIAL => Err(VfsError::InvalidInput), + Errnum::ELINKFIAL => Err(VfsError::InvalidInput), + + _ => Err(VfsError::InvalidInput), + } + } else { + drop(ext4_file); + // log::error!("file found"); + Ok(self.clone()) + } + } + + /// Create a new node with the given `path` in the directory + /// + /// Return [`Ok(())`](Ok) if it already exists. + fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult { + log::error!("create {:x?}", path); + + let types = match ty { + VfsNodeType::Fifo => DirEntryType::EXT4_DE_FIFO, + VfsNodeType::CharDevice => DirEntryType::EXT4_DE_CHRDEV, + VfsNodeType::Dir => DirEntryType::EXT4_DE_DIR, + VfsNodeType::BlockDevice => DirEntryType::EXT4_DE_BLKDEV, + VfsNodeType::File => DirEntryType::EXT4_DE_REG_FILE, + VfsNodeType::SymLink => DirEntryType::EXT4_DE_SYMLINK, + VfsNodeType::Socket => DirEntryType::EXT4_DE_SOCK, + }; + + let mut ext4file = self.ext4_file.lock(); + + if types == DirEntryType::EXT4_DE_DIR { + let r = self.ext4.ext4_dir_mk(path); + } else { + let r = self.ext4.ext4_open(&mut ext4file, path, "w+", true); + } + + drop(ext4file); + + Ok(()) + } + + /// Remove the node with the given `path` in the directory. + fn remove(&self, _path: &str) -> VfsResult { + todo!() + } + + /// Read directory entries into `dirents`, starting from `start_idx`. + fn read_dir(&self, start_idx: usize, dirents: &mut [VfsDirEntry]) -> VfsResult { + let ext4_file = self.ext4_file.lock(); + log::error!("read_dir_inode: {:?}", ext4_file.inode); + let inode_num = ext4_file.inode; + let entries: Vec = self.ext4.read_dir_entry(inode_num as _); + + let len = entries.len(); + let mut iter = entries.into_iter().skip(start_idx); + + for (i, out_entry) in dirents.iter_mut().enumerate() { + let x: Option = iter.next(); + match x { + Some(ext4direntry) => { + let name = ext4direntry.name; + let name_len = ext4direntry.name_len; + let file_type = unsafe { ext4direntry.inner.inode_type }; + let (ty, _) = map_dir_imode(file_type as u16); + let name = get_name(name, name_len as usize).unwrap(); + log::error!("de name {:}", name); + *out_entry = VfsDirEntry::new(name.as_str(), ty); + } + _ => return Ok(i), + } + } + + drop(ext4_file); + Ok(len) + } + + /// Renames or moves existing file or directory. + fn rename(&self, _src_path: &str, _dst_path: &str) -> VfsResult { + todo!() + } + + /// Convert `&self` to [`&dyn Any`][1] that can use + /// [`Any::downcast_ref`][2]. + /// + /// [1]: core::any::Any + /// [2]: core::any::Any#method.downcast_ref + fn as_any(&self) -> &dyn core::any::Any { + unimplemented!() + } +} + +fn map_dir_imode(imode: u16) -> (VfsNodeType, VfsNodePerm) { + let diren_type = imode; + let type_code = ext4_rs::DirEntryType::from_bits(diren_type as u8).unwrap(); + let ty = match type_code { + DirEntryType::EXT4_DE_REG_FILE => VfsNodeType::File, + DirEntryType::EXT4_DE_DIR => VfsNodeType::Dir, + DirEntryType::EXT4_DE_CHRDEV => VfsNodeType::CharDevice, + DirEntryType::EXT4_DE_BLKDEV => VfsNodeType::BlockDevice, + DirEntryType::EXT4_DE_FIFO => VfsNodeType::Fifo, + DirEntryType::EXT4_DE_SOCK => VfsNodeType::Socket, + DirEntryType::EXT4_DE_SYMLINK => VfsNodeType::SymLink, + _ => { + log::info!("{:x?}", imode); + VfsNodeType::File + } + }; + + let perm = ext4_rs::FileMode::from_bits_truncate(imode); + let mut vfs_perm = VfsNodePerm::from_bits_truncate(0); + + if perm.contains(ext4_rs::FileMode::S_IXOTH) { + vfs_perm |= VfsNodePerm::OTHER_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWOTH) { + vfs_perm |= VfsNodePerm::OTHER_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IROTH) { + vfs_perm |= VfsNodePerm::OTHER_READ; + } + + if perm.contains(ext4_rs::FileMode::S_IXGRP) { + vfs_perm |= VfsNodePerm::GROUP_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWGRP) { + vfs_perm |= VfsNodePerm::GROUP_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IRGRP) { + vfs_perm |= VfsNodePerm::GROUP_READ; + } + + if perm.contains(ext4_rs::FileMode::S_IXUSR) { + vfs_perm |= VfsNodePerm::OWNER_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWUSR) { + vfs_perm |= VfsNodePerm::OWNER_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IRUSR) { + vfs_perm |= VfsNodePerm::OWNER_READ; + } + + (ty, vfs_perm) +} + +fn map_imode(imode: u16) -> (VfsNodeType, VfsNodePerm) { + let file_type = (imode & 0xf000) as usize; + let ty = match file_type { + EXT4_INODE_MODE_FIFO => VfsNodeType::Fifo, + EXT4_INODE_MODE_CHARDEV => VfsNodeType::CharDevice, + EXT4_INODE_MODE_DIRECTORY => VfsNodeType::Dir, + EXT4_INODE_MODE_BLOCKDEV => VfsNodeType::BlockDevice, + EXT4_INODE_MODE_FILE => VfsNodeType::File, + EXT4_INODE_MODE_SOFTLINK => VfsNodeType::SymLink, + EXT4_INODE_MODE_SOCKET => VfsNodeType::Socket, + _ => { + log::info!("{:x?}", imode); + VfsNodeType::File + } + }; + + let perm = ext4_rs::FileMode::from_bits_truncate(imode); + let mut vfs_perm = VfsNodePerm::from_bits_truncate(0); + + if perm.contains(ext4_rs::FileMode::S_IXOTH) { + vfs_perm |= VfsNodePerm::OTHER_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWOTH) { + vfs_perm |= VfsNodePerm::OTHER_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IROTH) { + vfs_perm |= VfsNodePerm::OTHER_READ; + } + + if perm.contains(ext4_rs::FileMode::S_IXGRP) { + vfs_perm |= VfsNodePerm::GROUP_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWGRP) { + vfs_perm |= VfsNodePerm::GROUP_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IRGRP) { + vfs_perm |= VfsNodePerm::GROUP_READ; + } + + if perm.contains(ext4_rs::FileMode::S_IXUSR) { + vfs_perm |= VfsNodePerm::OWNER_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWUSR) { + vfs_perm |= VfsNodePerm::OWNER_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IRUSR) { + vfs_perm |= VfsNodePerm::OWNER_READ; + } + + (ty, vfs_perm) +} diff --git a/modules/axfs/src/fs/mod.rs b/modules/axfs/src/fs/mod.rs index fabe15976f..b1f4809db9 100644 --- a/modules/axfs/src/fs/mod.rs +++ b/modules/axfs/src/fs/mod.rs @@ -12,6 +12,9 @@ cfg_if::cfg_if! { } } +#[cfg(feature = "ext4_rs")] +pub mod ext4; + #[cfg(feature = "devfs")] pub use axfs_devfs as devfs; diff --git a/modules/axfs/src/root.rs b/modules/axfs/src/root.rs index 86b14f7888..17877541f4 100644 --- a/modules/axfs/src/root.rs +++ b/modules/axfs/src/root.rs @@ -170,6 +170,10 @@ pub(crate) fn init_rootfs(disk: crate::dev::Disk) { static EXT4_FS: LazyInit> = LazyInit::new(); EXT4_FS.init_by(Arc::new(fs::ext4fs::Ext4FileSystem::new(disk))); let main_fs = EXT4_FS.clone(); + } else if #[cfg(feature = "ext4_rs")] { + static EXT4_FS: LazyInit> = LazyInit::new(); + EXT4_FS.init_by(Arc::new(fs::ext4::Ext4FileSystem::new(disk))); + let main_fs = EXT4_FS.clone(); } } diff --git a/scripts/make/features.mk b/scripts/make/features.mk index 737a297abb..809aead3bf 100644 --- a/scripts/make/features.mk +++ b/scripts/make/features.mk @@ -46,6 +46,9 @@ else ifneq ($(findstring monolithic,$(APP)),) ifeq ($(filter ext4fs,$(FEATURES)),) override FEATURES += fatfs endif + ifeq ($(filter ext4_rs,$(FEATURES)),) + override FEATURES += ext4_rs + endif endif override FEATURES := $(strip $(FEATURES)) diff --git a/ulib/axstarry/Cargo.toml b/ulib/axstarry/Cargo.toml index 47395421c2..f7e614c675 100644 --- a/ulib/axstarry/Cargo.toml +++ b/ulib/axstarry/Cargo.toml @@ -45,6 +45,7 @@ fd = [] fs = ["axruntime/fs", "arceos_api/fs", "fd"] fatfs = ["axfeat/fatfs"] ext4fs = ["axfeat/ext4fs", "dep:axlibc"] +ext4_rs = ["axfeat/ext4_rs"] # Signal signal = ["axfeat/signal", "dep:axsignal"] From 2345ffebebeb5606713d8ccbc3901f200f5996ed Mon Sep 17 00:00:00 2001 From: yuoo655 Date: Sat, 1 Jun 2024 21:28:21 +0800 Subject: [PATCH 2/3] fix read_dir return value --- modules/axfs/src/fs/ext4.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/axfs/src/fs/ext4.rs b/modules/axfs/src/fs/ext4.rs index 913d49af37..d7e659582c 100644 --- a/modules/axfs/src/fs/ext4.rs +++ b/modules/axfs/src/fs/ext4.rs @@ -300,7 +300,7 @@ impl VfsNodeOps for Ext4FileWrapper { } drop(ext4_file); - Ok(len) + Ok(dirents.len()) } /// Renames or moves existing file or directory. From 7d1c950ab8884c782d5c58c7896340e4d6463c9f Mon Sep 17 00:00:00 2001 From: yuoo655 Date: Sat, 1 Jun 2024 21:33:13 +0800 Subject: [PATCH 3/3] impl as_any --- modules/axfs/src/fs/ext4.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/modules/axfs/src/fs/ext4.rs b/modules/axfs/src/fs/ext4.rs index d7e659582c..88e5b09615 100644 --- a/modules/axfs/src/fs/ext4.rs +++ b/modules/axfs/src/fs/ext4.rs @@ -308,13 +308,8 @@ impl VfsNodeOps for Ext4FileWrapper { todo!() } - /// Convert `&self` to [`&dyn Any`][1] that can use - /// [`Any::downcast_ref`][2]. - /// - /// [1]: core::any::Any - /// [2]: core::any::Any#method.downcast_ref fn as_any(&self) -> &dyn core::any::Any { - unimplemented!() + self as &dyn core::any::Any } }