Ver código fonte

feat(fs): 补充mount系统调用,增加对硬盘挂载 & ext4文件系统的支持 (#1182)

主要变更:
* 实现完整的mount系统调用,支持从块设备挂载文件系统
* 新增ext4文件系统支持,基于another_ext4库实现
* 引入MountableFileSystem trait和文件系统工厂模式,提升VFS架构
* 完善块设备管理,自动将磁盘和分区注册到devfs(/dev)
* 支持virtio块设备的分区检测和挂载
* 新增umount2系统调用支持文件系统卸载
* 重构symlink相关系统调用,提升代码组织
* 提供硬盘镜像制作脚本和测试程序

技术细节:
- 支持ext4和vfat文件系统的挂载
- 实现MBR分区表解析和GenDisk管理
- 集成页面缓存支持提升文件系统性能
- 完善错误处理和设备号管理
- 新增详细的VFS挂载机制文档

测试验证:
- 新增test-mount-ext4和test-mount-fat测试程序
- 提供make_fs_image.sh脚本创建测试镜像
- 验证挂载、读写、卸载完整流程

Co-authored-by: Samuka007 <samuka007@dragon-os.org>
Co-authored-by: oeasy1412 <oeasy1412@gmail.com>
Co-authored-by: fslongjin <longjin@DragonOS.org>
火花 2 dias atrás
pai
commit
1e574d89fa
49 arquivos alterados com 2211 adições e 367 exclusões
  1. 2 0
      docs/kernel/filesystem/vfs/index.rst
  2. 93 0
      docs/kernel/filesystem/vfs/mountable_fs.md
  3. 11 0
      kernel/Cargo.lock
  4. 0 1
      kernel/Cargo.toml
  5. 1 0
      kernel/crates/kdepends/Cargo.toml
  6. 2 0
      kernel/crates/kdepends/src/lib.rs
  7. 2 1
      kernel/crates/system_error/Cargo.toml
  8. 7 0
      kernel/crates/system_error/src/another_ext4.rs
  9. 2 0
      kernel/crates/system_error/src/lib.rs
  10. 106 2
      kernel/src/driver/base/block/gendisk/mod.rs
  11. 68 18
      kernel/src/driver/base/block/manager.rs
  12. 12 0
      kernel/src/driver/base/device/device_number.rs
  13. 5 0
      kernel/src/driver/base/device/mod.rs
  14. 57 3
      kernel/src/driver/block/virtio_blk.rs
  15. 2 1
      kernel/src/driver/disk/ahci/ahcidisk.rs
  16. 4 2
      kernel/src/driver/virtio/virtio.rs
  17. 114 15
      kernel/src/filesystem/devfs/mod.rs
  18. 1 1
      kernel/src/filesystem/devpts/mod.rs
  19. 159 0
      kernel/src/filesystem/ext4/filesystem.rs
  20. 49 0
      kernel/src/filesystem/ext4/gendisk.rs
  21. 389 0
      kernel/src/filesystem/ext4/inode.rs
  22. 4 0
      kernel/src/filesystem/ext4/mod.rs
  23. 1 0
      kernel/src/filesystem/fat/mod.rs
  24. 74 0
      kernel/src/filesystem/fat/mount.rs
  25. 1 0
      kernel/src/filesystem/mod.rs
  26. 24 17
      kernel/src/filesystem/overlayfs/mod.rs
  27. 14 10
      kernel/src/filesystem/ramfs/mod.rs
  28. 109 26
      kernel/src/filesystem/vfs/mod.rs
  29. 35 1
      kernel/src/filesystem/vfs/mount.rs
  30. 11 85
      kernel/src/filesystem/vfs/syscall/mod.rs
  31. 50 0
      kernel/src/filesystem/vfs/syscall/symlink_utils.rs
  32. 137 0
      kernel/src/filesystem/vfs/syscall/sys_mount.rs
  33. 61 0
      kernel/src/filesystem/vfs/syscall/sys_symlink.rs
  34. 67 0
      kernel/src/filesystem/vfs/syscall/sys_symlinkat.rs
  35. 104 0
      kernel/src/filesystem/vfs/syscall/sys_umount2.rs
  36. 8 154
      kernel/src/filesystem/vfs/vcore.rs
  37. 0 30
      kernel/src/syscall/mod.rs
  38. 64 0
      tools/make_fs_image.sh
  39. 16 0
      tools/run-qemu.sh
  40. 3 0
      user/apps/test-mount-ext4/.gitignore
  41. 12 0
      user/apps/test-mount-ext4/Cargo.toml
  42. 56 0
      user/apps/test-mount-ext4/Makefile
  43. 50 0
      user/apps/test-mount-ext4/src/main.rs
  44. 3 0
      user/apps/test-mount-fat/.gitignore
  45. 12 0
      user/apps/test-mount-fat/Cargo.toml
  46. 56 0
      user/apps/test-mount-fat/Makefile
  47. 51 0
      user/apps/test-mount-fat/src/main.rs
  48. 51 0
      user/dadk/config/test_mount_ext4_0_1_0.toml
  49. 51 0
      user/dadk/config/test_mount_fat_0_1_0.toml

+ 2 - 0
docs/kernel/filesystem/vfs/index.rst

@@ -12,6 +12,7 @@ VFS是DragonOS文件系统的核心,它提供了一套统一的文件系统接
 - 提供文件系统的抽象(FileSystem)
 - 提供IndexNode抽象
 - 提供文件系统的缓存、同步机制(尚未实现)
+- 支持将硬盘设备挂载到文件系统上(目前支持EXT4和vfat类型的virtio硬盘)
 
 
 .. toctree::
@@ -20,4 +21,5 @@ VFS是DragonOS文件系统的核心,它提供了一套统一的文件系统接
 
    design
    api
+   mountable_fs
 

+ 93 - 0
docs/kernel/filesystem/vfs/mountable_fs.md

@@ -0,0 +1,93 @@
+:::{note}
+本文作者: 庄凯烨
+
+Email: <sparkhhhhhhhhhh@outlook.com>
+:::
+
+# 设计
+```mermaid
+    graph TD
+    subgraph 用户层 / 系统调用
+        A[sys_mount] --> B[produce_fs!]
+    end
+
+    subgraph 文件系统注册与创建
+        B --> C{查找 FSMAKER}
+        C -- 找到匹配 --> D[FileSystemMaker::builder && FileSystemMaker::maker]
+        D --> G[FileSystem 实例]
+        G --> H[挂载成功]
+        C -- 未找到 --> I[错误: EINVAL]
+    end
+
+    subgraph 文件系统实现者
+        J[RamFS / 其他文件系统] --> K[实现 MountableFileSystem trait]
+        K --> L[make_mount_data]
+        K --> M[make_fs]
+        L --> N[register_mountable_fs!宏]
+        M --> N
+    end
+
+    N --> O[分布式切片 FSMAKER ]
+    O --> C
+
+    click J "#" "RamFS - 文件系统示例"
+    click B "#" "produce_fs 函数"
+    click O "#" "FSMAKER - 文件系统工厂数组"
+```
+## 流程说明:
+
+
+- 具体的文件系统(例如`RamFS`)通过实现```MountableFileSystem trait```,并使用 ```register_mountable_fs!``` 宏,将自身的创建逻辑注册到 `FSMAKER` 中。
+
+
+- 用户通过 `sys_mount` 系统调用请求挂载一个文件系统。
+
+- `sys_mount` 调用 `produce_fs` 函数,传入文件系统类型、原始挂载数据和源路径。
+
+- `produce_fs` 遍历全局的 `FSMAKER` 数组,查找与请求的文件系统类型名称匹配的 FileSystemMaker。
+
+- 如果找到,首先调用 `maker.builder`(它内部会调用具体文件系统的 `make_mount_data` 方法)来处理原始数据,生成一个可选的 `mount_data` 对象。
+
+- 接着,调用 `maker.build`(它内部会调用具体文件系统的 `make_fs` 方法),并传入上一步生成的 mount_data,从而创建出文件系统实例。
+
+- 成功创建的文件系统实例(`Arc<dyn FileSystem>`)被返回并用于后续的挂载操作。
+
+- 如果找不到对应的文件系统类型,则返回错误。
+
+## 其他
+
+目前 DragonOS 支持挂载的文件系统包括 `ramfs`、`ext4` 和 `vfat`。在 DragonOS 中挂载硬盘文件时,要注意:
+- 由于系统暂时无法直接查看硬盘的文件系统类型,在挂载前需要提前明确目标分区所使用的文件系统类型。
+- 挂载操作需要指定对应的硬盘设备名称(位于 /dev 下)。
+- 这些硬盘设备文件来源于通过修改 `tools/run-qemu.sh` 启动脚本,将制作好的硬盘镜像文件传入系统。virtio 硬盘设备命名示例如 `vda1`、`vdb1`,硬盘在 DragonOS 内的设备名称是根据 `run-qemu.sh` 中镜像传入的顺序自动分配(a,b,c等等)的,其中的数字表示分区号。
+
+所以目前需要挂载硬盘的话,可以更改`test-mount-ext4`执行程序,将指定的硬盘文件以对应的文件系统格式进行挂载,以下为挂载示例:
+
+
+```Rust
+use core::ffi::{c_char, c_void};
+use libc::{mount, MS_BIND};
+use std::fs;
+use std::path::Path;
+
+fn main() {
+    let ext4_path = Path::new("mnt/ext4");
+    let dir = fs::create_dir_all(ext4_path);
+    if dir.is_err() {
+        panic!("mkdir /mnt/ext4 fail.");
+    }
+
+    // 硬盘名称,由传入顺序决定
+    let source = b"/dev/vdb1\0".as_ptr() as *const c_char;
+    let target = b"/mnt/ext4\0".as_ptr() as *const c_char;
+    // 文件系统类型
+    let fstype = b"ext4\0".as_ptr() as *const c_char;
+    let flags = MS_BIND;
+    let data = std::ptr::null() as *const c_void;
+    let _ = unsafe { mount(source, target, fstype, flags, data) };
+
+    println!("Mount successfully!");
+}
+```
+
+至于硬盘镜像的制作,可以通过运行`sudo bash tools/make_fs_image.sh`来同时制作`ext4`和`fat`的磁盘镜像,进入系统后就可以dev目录下多出两个磁盘文件(`vdb1`和`vdc1`),然后就可以执行`test-mount-ext4`以及`test-mount-fat`测试程序来验证

+ 11 - 0
kernel/Cargo.lock

@@ -39,6 +39,15 @@ version = "0.2.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
 
+[[package]]
+name = "another_ext4"
+version = "0.1.0"
+source = "git+https://git.mirrors.dragonos.org.cn/DragonOS-Community/another_ext4.git?rev=bf782ff294#bf782ff2947b57ba89503824eada5eb3c20a2e2a"
+dependencies = [
+ "bitflags 2.9.0",
+ "log",
+]
+
 [[package]]
 name = "asm_macros"
 version = "0.1.0"
@@ -608,6 +617,7 @@ version = "0.1.0"
 name = "kdepends"
 version = "0.1.0"
 dependencies = [
+ "another_ext4",
  "crc",
  "memoffset",
  "ringbuffer",
@@ -1299,6 +1309,7 @@ version = "0.1.0"
 name = "system_error"
 version = "0.1.0"
 dependencies = [
+ "kdepends",
  "num-derive",
  "num-traits 0.2.15",
 ]

+ 0 - 1
kernel/Cargo.toml

@@ -86,7 +86,6 @@ derive_builder = { version = "0.20.2", default-features = false, features = [
     "alloc",
 ] }
 
-
 # target为x86_64时,使用下面的依赖
 [target.'cfg(target_arch = "x86_64")'.dependencies]
 multiboot2 = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/multiboot2", rev = "05739aab40" }

+ 1 - 0
kernel/crates/kdepends/Cargo.toml

@@ -11,6 +11,7 @@ crc = { path = "../crc" }
 memoffset = "0.9.0"
 ringbuffer = "0.15.0"
 xarray = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/xarray", rev = "de93b57c34", features = ["slab-friendly"] }
+another_ext4 = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/another_ext4.git", rev = "bf782ff294", default-features = false }
 
 # 一个无锁MPSC队列
 [dependencies.thingbuf]

+ 2 - 0
kernel/crates/kdepends/src/lib.rs

@@ -8,3 +8,5 @@ pub extern crate ringbuffer;
 
 pub extern crate crc;
 pub extern crate xarray;
+
+pub extern crate another_ext4;

+ 2 - 1
kernel/crates/system_error/Cargo.toml

@@ -6,5 +6,6 @@ edition = "2021"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-num-traits = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/num-traits.git", rev="1597c1c", default-features = false }
+num-traits = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/num-traits.git", rev = "1597c1c", default-features = false }
 num-derive = "0.3"
+kdepends = { path = "../kdepends" }

+ 7 - 0
kernel/crates/system_error/src/another_ext4.rs

@@ -0,0 +1,7 @@
+use kdepends::another_ext4::Ext4Error;
+
+impl From<Ext4Error> for super::SystemError {
+    fn from(err: Ext4Error) -> Self {
+        <Self as num_traits::FromPrimitive>::from_i32(err.code() as i32).unwrap()
+    }
+}

+ 2 - 0
kernel/crates/system_error/src/lib.rs

@@ -4,6 +4,8 @@
 #![allow(non_local_definitions)]
 use num_derive::{FromPrimitive, ToPrimitive};
 
+mod another_ext4;
+
 #[repr(i32)]
 #[derive(Debug, FromPrimitive, ToPrimitive, PartialEq, Eq, Clone)]
 #[allow(dead_code, non_camel_case_types)]

+ 106 - 2
kernel/src/driver/base/block/gendisk.rs → kernel/src/driver/base/block/gendisk/mod.rs

@@ -3,18 +3,39 @@ use core::{
     sync::atomic::{AtomicU32, Ordering},
 };
 
-use alloc::sync::{Arc, Weak};
+use alloc::{
+    string::String,
+    sync::{Arc, Weak},
+};
 use hashbrown::HashMap;
 use system_error::SystemError;
 
+use crate::{
+    driver::base::device::device_number::DeviceNumber,
+    filesystem::{
+        devfs::{DevFS, DeviceINode},
+        vfs::{syscall::ModeType, utils::DName, IndexNode, Metadata},
+    },
+    libs::{rwlock::RwLock, spinlock::SpinLockGuard},
+};
+
 use super::block_device::{BlockDevice, BlockId, GeneralBlockRange, LBA_SIZE};
 
+const MINORS_PER_DISK: u32 = 256;
+
 #[derive(Debug)]
 pub struct GenDisk {
     bdev: Weak<dyn BlockDevice>,
     range: GeneralBlockRange,
     block_size_log2: u8,
     idx: Option<u32>,
+
+    device_num: DeviceNumber,
+
+    fs: RwLock<Weak<DevFS>>,
+    metadata: Metadata,
+    /// 对应/dev/下的设备名
+    name: DName,
 }
 
 impl GenDisk {
@@ -25,14 +46,35 @@ impl GenDisk {
         bdev: Weak<dyn BlockDevice>,
         range: GeneralBlockRange,
         idx: Option<u32>,
+        dev_name: DName,
     ) -> Arc<Self> {
         let bsizelog2 = bdev.upgrade().unwrap().blk_size_log2();
 
+        // 对应整块硬盘的情况
+        let id = idx.unwrap_or(0);
+        if id >= MINORS_PER_DISK {
+            panic!("GenDisk index out of range: {}", id);
+        }
+        let ptr = bdev.upgrade().unwrap();
+        let meta = ptr.blkdev_meta();
+        let major = meta.major;
+
+        let minor = meta.base_minor * MINORS_PER_DISK + id;
+        // log::info!("New gendisk: major: {}, minor: {}", major, minor);
+        let device_num = DeviceNumber::new(major, minor);
+
         return Arc::new(GenDisk {
             bdev,
             range,
             block_size_log2: bsizelog2,
             idx,
+            device_num,
+            fs: RwLock::new(Weak::default()),
+            metadata: Metadata::new(
+                crate::filesystem::vfs::FileType::BlockDevice,
+                ModeType::from_bits_truncate(0o755),
+            ),
+            name: dev_name,
         });
     }
 
@@ -120,7 +162,7 @@ impl GenDisk {
     }
 
     #[inline]
-    fn block_offset_2_disk_blkid(&self, block_offset: BlockId) -> BlockId {
+    pub fn block_offset_2_disk_blkid(&self, block_offset: BlockId) -> BlockId {
         self.range.lba_start + block_offset
     }
 
@@ -139,11 +181,73 @@ impl GenDisk {
         &self.range
     }
 
+    #[inline]
+    pub fn device_num(&self) -> DeviceNumber {
+        self.device_num
+    }
+
+    #[inline]
+    pub fn minor(&self) -> u32 {
+        self.device_num.minor()
+    }
+
     /// # sync
     /// 同步磁盘
     pub fn sync(&self) -> Result<(), SystemError> {
         self.block_device().sync()
     }
+
+    pub fn symlink_name(&self) -> String {
+        let major = self.device_num.major().data();
+        let minor = self.device_num.minor();
+        format!("{}:{}", major, minor)
+    }
+
+    pub fn block_size_log2(&self) -> u8 {
+        self.block_size_log2
+    }
+}
+
+impl IndexNode for GenDisk {
+    fn fs(&self) -> Arc<dyn crate::filesystem::vfs::FileSystem> {
+        self.fs.read().upgrade().unwrap()
+    }
+    fn as_any_ref(&self) -> &dyn core::any::Any {
+        self
+    }
+    fn read_at(
+        &self,
+        _offset: usize,
+        _len: usize,
+        _buf: &mut [u8],
+        _data: SpinLockGuard<crate::filesystem::vfs::FilePrivateData>,
+    ) -> Result<usize, SystemError> {
+        Err(SystemError::EPERM)
+    }
+    fn write_at(
+        &self,
+        _offset: usize,
+        _len: usize,
+        _buf: &[u8],
+        _data: SpinLockGuard<crate::filesystem::vfs::FilePrivateData>,
+    ) -> Result<usize, SystemError> {
+        Err(SystemError::EPERM)
+    }
+    fn list(&self) -> Result<alloc::vec::Vec<alloc::string::String>, system_error::SystemError> {
+        Err(SystemError::ENOSYS)
+    }
+    fn metadata(&self) -> Result<crate::filesystem::vfs::Metadata, SystemError> {
+        Ok(self.metadata.clone())
+    }
+    fn dname(&self) -> Result<DName, SystemError> {
+        Ok(self.name.clone())
+    }
+}
+
+impl DeviceINode for GenDisk {
+    fn set_fs(&self, fs: alloc::sync::Weak<crate::filesystem::devfs::DevFS>) {
+        *self.fs.write() = fs;
+    }
 }
 
 #[derive(Default)]

+ 68 - 18
kernel/src/driver/base/block/manager.rs

@@ -1,4 +1,4 @@
-use core::fmt::Formatter;
+use core::{fmt::Formatter, sync::atomic::AtomicU32};
 
 use alloc::sync::Arc;
 use hashbrown::HashMap;
@@ -6,8 +6,15 @@ use system_error::SystemError;
 use unified_init::macros::unified_init;
 
 use crate::{
-    driver::base::{block::gendisk::GenDisk, device::DevName},
-    filesystem::mbr::MbrDiskPartionTable,
+    driver::base::{
+        block::gendisk::GenDisk,
+        device::{device_number::Major, DevName},
+    },
+    filesystem::{
+        devfs::devfs_register,
+        mbr::MbrDiskPartionTable,
+        vfs::{utils::DName, IndexNode},
+    },
     init::initcall::INITCALL_POSTCORE,
     libs::spinlock::{SpinLock, SpinLockGuard},
 };
@@ -39,12 +46,15 @@ pub struct BlockDevManager {
 
 struct InnerBlockDevManager {
     disks: HashMap<DevName, Arc<dyn BlockDevice>>,
+    /// 记录每个major对应的下一个可用的minor号
+    minors: HashMap<Major, AtomicU32>,
 }
 impl BlockDevManager {
     pub fn new() -> Self {
         BlockDevManager {
             inner: SpinLock::new(InnerBlockDevManager {
                 disks: HashMap::new(),
+                minors: HashMap::new(),
             }),
         }
     }
@@ -62,18 +72,18 @@ impl BlockDevManager {
         }
         inner.disks.insert(dev_name.clone(), dev.clone());
 
-        let mut out_remove = || {
+        // 检测分区表,并创建gendisk
+        let res = self.check_partitions(&dev);
+        if res.is_err() {
             inner.disks.remove(dev_name);
         };
-
-        // 检测分区表,并创建gendisk
-        self.check_partitions(&dev).inspect_err(|_| out_remove())?;
+        res?;
         Ok(())
     }
 
     /// 检测分区表,并创建gendisk
     fn check_partitions(&self, dev: &Arc<dyn BlockDevice>) -> Result<(), SystemError> {
-        if self.check_mbr(dev).is_ok() {
+        if self.try_register_disk_by_mbr(dev).is_ok() {
             return Ok(());
         }
 
@@ -81,11 +91,13 @@ impl BlockDevManager {
         self.register_entire_disk_as_gendisk(dev)
     }
 
-    fn check_mbr(&self, dev: &Arc<dyn BlockDevice>) -> Result<(), SystemError> {
+    fn try_register_disk_by_mbr(&self, dev: &Arc<dyn BlockDevice>) -> Result<(), SystemError> {
         let mbr = MbrDiskPartionTable::from_disk(dev.clone())?;
         let piter = mbr.partitions_raw();
+        let mut idx;
         for p in piter {
-            self.register_gendisk_with_range(dev, p.try_into()?)?;
+            idx = dev.blkdev_meta().inner().gendisks.alloc_idx();
+            self.register_gendisk_with_range(dev, p.try_into()?, idx)?;
         }
         Ok(())
     }
@@ -96,20 +108,29 @@ impl BlockDevManager {
         dev: &Arc<dyn BlockDevice>,
     ) -> Result<(), SystemError> {
         let range = dev.disk_range();
-        self.register_gendisk_with_range(dev, range)
+        self.register_gendisk_with_range(dev, range, GenDisk::ENTIRE_DISK_IDX)
     }
 
     fn register_gendisk_with_range(
         &self,
         dev: &Arc<dyn BlockDevice>,
         range: GeneralBlockRange,
+        idx: u32,
     ) -> Result<(), SystemError> {
         let weak_dev = Arc::downgrade(dev);
-        let gendisk = GenDisk::new(
-            weak_dev,
-            range,
-            Some(dev.blkdev_meta().inner().gendisks.alloc_idx()),
-        );
+
+        // 这里先拿到硬盘的设备名,然后在根据idx来生成gendisk的名字
+        // 如果是整个磁盘,则idx为 None,名字为/dev/sda
+        // 如果是分区,例如idx为1,则名字为/dev/sda1
+        // 以此类推
+        let dev_name = dev.dev_name();
+        let (idx, dev_name) = match idx {
+            GenDisk::ENTIRE_DISK_IDX => (None, DName::from(dev_name.name())),
+            id => (Some(id), DName::from(format!("{}{}", dev_name.name(), idx))),
+        };
+
+        let gendisk = GenDisk::new(weak_dev, range, idx, dev_name);
+        // log::info!("Registering gendisk");
         self.register_gendisk(dev, gendisk)
     }
 
@@ -130,6 +151,17 @@ impl BlockDevManager {
         dev.callback_gendisk_registered(&gendisk).inspect_err(|_| {
             meta_inner.gendisks.remove(&idx);
         })?;
+
+        // 注册到devfs
+        let dname = gendisk.dname()?;
+        devfs_register(dname.as_ref(), gendisk.clone()).map_err(|e| {
+            log::error!(
+                "Failed to register gendisk {:?} to devfs: {:?}",
+                dname.as_ref(),
+                e
+            );
+            e
+        })?;
         Ok(())
     }
 
@@ -204,28 +236,46 @@ impl BlockDevManager {
 
         Some((path, partno))
     }
+
+    /// 获取对应major下一个可用的minor号
+    pub(self) fn next_minor(&self, major: Major) -> u32 {
+        let mut inner = self.inner();
+        let base = inner
+            .minors
+            .entry(major)
+            .or_insert_with(|| AtomicU32::new(0));
+        let base_minor = base.load(core::sync::atomic::Ordering::SeqCst);
+        base.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
+        base_minor
+    }
 }
 
 pub struct BlockDevMeta {
     pub devname: DevName,
+    pub major: Major,
+    pub base_minor: u32,
     inner: SpinLock<InnerBlockDevMeta>,
 }
 
 pub struct InnerBlockDevMeta {
     pub gendisks: GenDiskMap,
+    pub dev_idx: usize,
 }
 
 impl BlockDevMeta {
-    pub fn new(devname: DevName) -> Self {
+    pub fn new(devname: DevName, major: Major) -> Self {
         BlockDevMeta {
             devname,
+            major,
+            base_minor: block_dev_manager().next_minor(major),
             inner: SpinLock::new(InnerBlockDevMeta {
                 gendisks: GenDiskMap::new(),
+                dev_idx: 0, // 默认索引为0
             }),
         }
     }
 
-    fn inner(&self) -> SpinLockGuard<InnerBlockDevMeta> {
+    pub(crate) fn inner(&self) -> SpinLockGuard<InnerBlockDevMeta> {
         self.inner.lock()
     }
 }

+ 12 - 0
kernel/src/driver/base/device/device_number.rs

@@ -1,3 +1,5 @@
+use core::hash::{Hash, Hasher};
+
 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
 pub struct Major(u32);
 
@@ -24,6 +26,10 @@ impl Major {
     pub const UNIX98_PTY_SLAVE_MAJOR: Self =
         Self::new(Self::UNIX98_PTY_MASTER_MAJOR.0 + Self::UNIX98_PTY_MAJOR_COUNT.0);
 
+    /// Disk
+    pub const AHCI_BLK_MAJOR: Self = Self::new(8);
+    pub const VIRTIO_BLK_MAJOR: Self = Self::new(254);
+
     pub const HVC_MAJOR: Self = Self::new(229);
 
     pub const fn new(x: u32) -> Self {
@@ -34,6 +40,12 @@ impl Major {
     }
 }
 
+impl Hash for Major {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        self.0.hash(state); // 使用 Major 内部的 u32 值来计算哈希值
+    }
+}
+
 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct DeviceNumber {
     data: u32,

+ 5 - 0
kernel/src/driver/base/device/mod.rs

@@ -142,6 +142,11 @@ impl DevName {
     pub fn id(&self) -> usize {
         return self.id;
     }
+
+    #[inline]
+    pub fn name(&self) -> &str {
+        return self.name.as_ref();
+    }
 }
 
 impl core::fmt::Debug for DevName {

+ 57 - 3
kernel/src/driver/block/virtio_blk.rs

@@ -25,6 +25,7 @@ use crate::{
             class::Class,
             device::{
                 bus::Bus,
+                device_number::Major,
                 driver::{Driver, DriverCommonData},
                 DevName, Device, DeviceCommonData, DeviceId, DeviceType, IdTable,
             },
@@ -40,10 +41,15 @@ use crate::{
         },
     },
     exception::{irqdesc::IrqReturn, IrqNumber},
-    filesystem::{kernfs::KernFSInode, mbr::MbrDiskPartionTable},
+    filesystem::{
+        devfs::{DevFS, DeviceINode},
+        kernfs::KernFSInode,
+        mbr::MbrDiskPartionTable,
+        vfs::{syscall::ModeType, IndexNode, Metadata},
+    },
     init::initcall::INITCALL_POSTCORE,
     libs::{
-        rwlock::{RwLockReadGuard, RwLockWriteGuard},
+        rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard},
         spinlock::{SpinLock, SpinLockGuard},
     },
 };
@@ -158,6 +164,9 @@ pub struct VirtIOBlkDevice {
     inner: SpinLock<InnerVirtIOBlkDevice>,
     locked_kobj_state: LockedKObjectState,
     self_ref: Weak<Self>,
+
+    fs: RwLock<Weak<DevFS>>,
+    metadata: Metadata,
 }
 
 impl Debug for VirtIOBlkDevice {
@@ -191,7 +200,7 @@ impl VirtIOBlkDevice {
         let mut device_inner: VirtIOBlk<HalImpl, VirtIOTransport> = device_inner.unwrap();
         device_inner.enable_interrupts();
         let dev = Arc::new_cyclic(|self_ref| Self {
-            blkdev_meta: BlockDevMeta::new(devname),
+            blkdev_meta: BlockDevMeta::new(devname, Major::VIRTIO_BLK_MAJOR),
             self_ref: self_ref.clone(),
             dev_id,
             locked_kobj_state: LockedKObjectState::default(),
@@ -203,6 +212,11 @@ impl VirtIOBlkDevice {
                 kobject_common: KObjectCommonData::default(),
                 irq,
             }),
+            fs: RwLock::new(Weak::default()),
+            metadata: Metadata::new(
+                crate::filesystem::vfs::FileType::BlockDevice,
+                ModeType::from_bits_truncate(0o755),
+            ),
         });
 
         Some(dev)
@@ -213,6 +227,45 @@ impl VirtIOBlkDevice {
     }
 }
 
+impl IndexNode for VirtIOBlkDevice {
+    fn fs(&self) -> Arc<dyn crate::filesystem::vfs::FileSystem> {
+        todo!()
+    }
+    fn as_any_ref(&self) -> &dyn core::any::Any {
+        self
+    }
+    fn read_at(
+        &self,
+        _offset: usize,
+        _len: usize,
+        _buf: &mut [u8],
+        _data: SpinLockGuard<crate::filesystem::vfs::FilePrivateData>,
+    ) -> Result<usize, SystemError> {
+        Err(SystemError::ENOSYS)
+    }
+    fn write_at(
+        &self,
+        _offset: usize,
+        _len: usize,
+        _buf: &[u8],
+        _data: SpinLockGuard<crate::filesystem::vfs::FilePrivateData>,
+    ) -> Result<usize, SystemError> {
+        Err(SystemError::ENOSYS)
+    }
+    fn list(&self) -> Result<alloc::vec::Vec<alloc::string::String>, system_error::SystemError> {
+        Err(SystemError::ENOSYS)
+    }
+    fn metadata(&self) -> Result<crate::filesystem::vfs::Metadata, SystemError> {
+        Ok(self.metadata.clone())
+    }
+}
+
+impl DeviceINode for VirtIOBlkDevice {
+    fn set_fs(&self, fs: alloc::sync::Weak<crate::filesystem::devfs::DevFS>) {
+        *self.fs.write() = fs;
+    }
+}
+
 impl BlockDevice for VirtIOBlkDevice {
     fn dev_name(&self) -> &DevName {
         &self.blkdev_meta.devname
@@ -342,6 +395,7 @@ impl VirtIODevice for VirtIOBlkDevice {
 
     fn set_virtio_device_index(&self, index: VirtIODeviceIndex) {
         self.inner().virtio_index = Some(index);
+        self.blkdev_meta.inner().dev_idx = index.into();
     }
 
     fn virtio_device_index(&self) -> Option<VirtIODeviceIndex> {

+ 2 - 1
kernel/src/driver/disk/ahci/ahcidisk.rs

@@ -6,6 +6,7 @@ use crate::driver::base::block::manager::BlockDevMeta;
 use crate::driver::base::class::Class;
 use crate::driver::base::device::bus::Bus;
 
+use crate::driver::base::device::device_number::Major;
 use crate::driver::base::device::driver::Driver;
 use crate::driver::base::device::{DevName, Device, DeviceType, IdTable};
 use crate::driver::base::kobject::{KObjType, KObject, KObjectState};
@@ -381,7 +382,7 @@ impl LockedAhciDisk {
         let devname = scsi_manager().alloc_id().ok_or(SystemError::EBUSY)?;
         // 构建磁盘结构体
         let result: Arc<LockedAhciDisk> = Arc::new_cyclic(|self_ref| LockedAhciDisk {
-            blkdev_meta: BlockDevMeta::new(devname),
+            blkdev_meta: BlockDevMeta::new(devname, Major::AHCI_BLK_MAJOR),
             inner: SpinLock::new(AhciDisk {
                 partitions: Vec::new(),
                 ctrl_num,

+ 4 - 2
kernel/src/driver/virtio/virtio.rs

@@ -36,8 +36,9 @@ fn virtio_probe() -> Result<(), SystemError> {
 fn virtio_probe_pci() {
     let virtio_list = virtio_device_search();
     for virtio_device in virtio_list {
-        let dev_id = virtio_device.common_header.device_id;
-        let dev_id = DeviceId::new(None, Some(format!("{dev_id}"))).unwrap();
+        let bdf: String = virtio_device.common_header.bus_device_function.into();
+        let dev_id = DeviceId::new(None, Some(bdf)).unwrap();
+        // log::info!("virtio device id: probe {:?}", dev_id.id());
         match PciTransport::new::<HalImpl>(virtio_device.clone(), dev_id.clone()) {
             Ok(mut transport) => {
                 debug!(
@@ -100,6 +101,7 @@ fn virtio_device_search() -> Vec<Arc<PciDeviceStructureGeneralDevice>> {
     for device in result {
         let standard_device = device.as_standard_device().unwrap();
         let header = &standard_device.common_header;
+        // log::info!("header: {:?}", header);
         if header.device_id >= 0x1000 && header.device_id <= 0x103F {
             virtio_list.push(standard_device);
         }

+ 114 - 15
kernel/src/filesystem/devfs/mod.rs

@@ -10,7 +10,7 @@ use super::vfs::{
     FilePrivateData, FileSystem, FileType, FsInfo, IndexNode, Magic, Metadata, SuperBlock,
 };
 use crate::{
-    driver::base::device::device_number::DeviceNumber,
+    driver::base::{block::gendisk::GenDisk, device::device_number::DeviceNumber},
     libs::{
         once::Once,
         spinlock::{SpinLock, SpinLockGuard},
@@ -174,7 +174,23 @@ impl DevFS {
                     .downcast_ref::<LockedDevFSInode>()
                     .unwrap();
 
-                dev_block_inode.add_dev(name, device.clone())?;
+                if name.starts_with("vd") && name.len() > 2 {
+                    // 虚拟磁盘设备挂载在 /dev 下
+                    dev_root_inode.add_dev(name, device.clone())?;
+                    let path = format!("/dev/{}", name);
+                    let symlink_name = device
+                        .as_any_ref()
+                        .downcast_ref::<GenDisk>()
+                        .unwrap()
+                        .symlink_name();
+
+                    dev_block_inode.add_dev_symlink(&path, &symlink_name)?;
+                } else if name.starts_with("nvme") {
+                    // NVMe设备挂载在 /dev 下
+                    dev_root_inode.add_dev(name, device.clone())?;
+                } else {
+                    dev_block_inode.add_dev(name, device.clone())?;
+                }
                 device.set_fs(dev_block_inode.0.lock().fs.clone());
             }
             FileType::KvmDevice => {
@@ -258,6 +274,8 @@ pub struct DevFSInode {
     metadata: Metadata,
     /// 目录名
     dname: DName,
+    /// 当前inode的数据部分(仅供symlink使用)
+    data: Vec<u8>,
 }
 
 impl DevFSInode {
@@ -294,6 +312,7 @@ impl DevFSInode {
             },
             fs: Weak::default(),
             dname: DName::default(),
+            data: Vec::new(),
         };
     }
 }
@@ -333,6 +352,28 @@ impl LockedDevFSInode {
         return Ok(());
     }
 
+    /// # 在devfs中添加一个符号链接
+    ///
+    /// ## 参数
+    /// - `path`: 符号链接指向的路径
+    /// - `symlink_name`: 符号链接的名称
+    pub fn add_dev_symlink(&self, path: &str, symlink_name: &str) -> Result<(), SystemError> {
+        let new_inode = self.create_with_data(
+            symlink_name,
+            FileType::SymLink,
+            ModeType::from_bits_truncate(0o777),
+            0,
+        )?;
+
+        let buf = path.as_bytes();
+        let len = buf.len();
+        new_inode
+            .downcast_ref::<LockedDevFSInode>()
+            .unwrap()
+            .write_at(0, len, buf, SpinLock::new(FilePrivateData::Unused).lock())?;
+        Ok(())
+    }
+
     pub fn remove(&self, name: &str) -> Result<(), SystemError> {
         let x = self
             .0
@@ -351,7 +392,7 @@ impl LockedDevFSInode {
         name: &str,
         file_type: FileType,
         mode: ModeType,
-        data: usize,
+        dev: usize,
     ) -> Result<Arc<dyn IndexNode>, SystemError> {
         if guard.metadata.file_type != FileType::Dir {
             return Err(SystemError::ENOTDIR);
@@ -382,10 +423,11 @@ impl LockedDevFSInode {
                 nlinks: 1,
                 uid: 0,
                 gid: 0,
-                raw_dev: DeviceNumber::from(data as u32),
+                raw_dev: DeviceNumber::from(dev as u32),
             },
             fs: guard.fs.clone(),
             dname: name.clone(),
+            data: Vec::new(),
         })));
 
         // 初始化inode的自引用的weak指针
@@ -541,27 +583,84 @@ impl IndexNode for LockedDevFSInode {
         return Ok(());
     }
 
-    /// 读设备 - 应该调用设备的函数读写,而不是通过文件系统读写
+    /// 读设备 - 应该调用设备的函数读写,而不是通过文件系统读写,仅支持符号链接的读取
     fn read_at(
         &self,
-        _offset: usize,
-        _len: usize,
-        _buf: &mut [u8],
+        offset: usize,
+        len: usize,
+        buf: &mut [u8],
         _data: SpinLockGuard<FilePrivateData>,
     ) -> Result<usize, SystemError> {
-        error!("DevFS: read_at is not supported!");
-        Err(SystemError::ENOSYS)
+        let meta = self.metadata()?;
+        match meta.file_type {
+            FileType::SymLink => {
+                if buf.len() < len {
+                    return Err(SystemError::EINVAL);
+                }
+                // 加锁
+                let inode = self.0.lock();
+
+                // 检查当前inode是否为一个文件夹,如果是的话,就返回错误
+                if inode.metadata.file_type == FileType::Dir {
+                    return Err(SystemError::EISDIR);
+                }
+
+                let start = inode.data.len().min(offset);
+                let end = inode.data.len().min(offset + len);
+
+                // buffer空间不足
+                if buf.len() < (end - start) {
+                    return Err(SystemError::ENOBUFS);
+                }
+
+                // 拷贝数据
+                let src = &inode.data[start..end];
+                buf[0..src.len()].copy_from_slice(src);
+                return Ok(src.len());
+            }
+            _ => {
+                error!("DevFS: read_at is not supported!");
+                Err(SystemError::ENOSYS)
+            }
+        }
     }
 
-    /// 写设备 - 应该调用设备的函数读写,而不是通过文件系统读写
+    /// 写设备 - 应该调用设备的函数读写,而不是通过文件系统读写,仅支持符号链接的写入
     fn write_at(
         &self,
-        _offset: usize,
-        _len: usize,
-        _buf: &[u8],
+        offset: usize,
+        len: usize,
+        buf: &[u8],
         _data: SpinLockGuard<FilePrivateData>,
     ) -> Result<usize, SystemError> {
-        Err(SystemError::ENOSYS)
+        let meta = self.metadata()?;
+        match meta.file_type {
+            FileType::SymLink => {
+                if buf.len() < len {
+                    return Err(SystemError::EINVAL);
+                }
+                let mut inode = self.0.lock();
+
+                if inode.metadata.file_type == FileType::Dir {
+                    return Err(SystemError::EISDIR);
+                }
+
+                let data: &mut Vec<u8> = &mut inode.data;
+
+                // 如果文件大小比原来的大,那就resize这个数组
+                if offset + len > data.len() {
+                    data.resize(offset + len, 0);
+                }
+
+                let target = &mut data[offset..offset + len];
+                target.copy_from_slice(&buf[0..len]);
+                return Ok(len);
+            }
+            _ => {
+                error!("DevFS: read_at is not supported!");
+                Err(SystemError::ENOSYS)
+            }
+        }
     }
 
     fn parent(&self) -> Result<Arc<dyn IndexNode>, SystemError> {

+ 1 - 1
kernel/src/filesystem/devpts/mod.rs

@@ -22,7 +22,7 @@ use crate::{
             tty_device::{PtyType, TtyDevice, TtyType},
         },
     },
-    filesystem::vfs::{syscall::ModeType, vcore::do_mount_mkdir, FileType},
+    filesystem::vfs::{mount::do_mount_mkdir, syscall::ModeType, FileType},
     init::initcall::INITCALL_FS,
     libs::spinlock::{SpinLock, SpinLockGuard},
     time::PosixTimeSpec,

+ 159 - 0
kernel/src/filesystem/ext4/filesystem.rs

@@ -0,0 +1,159 @@
+use crate::driver::base::block::gendisk::GenDisk;
+use crate::driver::base::device::device_number::DeviceNumber;
+use crate::filesystem::ext4::inode::Ext4Inode;
+use crate::filesystem::vfs::fcntl::AtFlags;
+use crate::filesystem::vfs::utils::{user_path_at, DName};
+use crate::filesystem::vfs::vcore::{generate_inode_id, try_find_gendisk};
+use crate::filesystem::vfs::{
+    self, FileSystem, FileSystemMaker, FileSystemMakerData, IndexNode, Magic, MountableFileSystem,
+    FSMAKER, VFS_MAX_FOLLOW_SYMLINK_TIMES,
+};
+use crate::libs::spinlock::SpinLock;
+use crate::mm::fault::{PageFaultHandler, PageFaultMessage};
+use crate::mm::VmFaultReason;
+use crate::process::ProcessManager;
+use crate::register_mountable_fs;
+use alloc::{
+    collections::BTreeMap,
+    sync::{Arc, Weak},
+};
+use kdepends::another_ext4;
+use linkme::distributed_slice;
+use system_error::SystemError;
+
+use super::inode::LockedExt4Inode;
+
+pub struct Ext4FileSystem {
+    /// 对应 another_ext4 中的实际文件系统
+    pub(super) fs: another_ext4::Ext4,
+    /// 当前文件系统对应的设备号
+    pub(super) raw_dev: DeviceNumber,
+
+    /// 根 inode
+    root_inode: Arc<LockedExt4Inode>,
+}
+
+impl FileSystem for Ext4FileSystem {
+    fn root_inode(&self) -> Arc<dyn IndexNode> {
+        self.root_inode.clone()
+    }
+
+    fn info(&self) -> vfs::FsInfo {
+        todo!()
+    }
+
+    fn as_any_ref(&self) -> &dyn core::any::Any {
+        self
+    }
+
+    fn name(&self) -> &str {
+        "ext4"
+    }
+
+    fn super_block(&self) -> vfs::SuperBlock {
+        vfs::SuperBlock::new(Magic::EXT4_MAGIC, another_ext4::BLOCK_SIZE as u64, 255)
+    }
+
+    unsafe fn fault(&self, pfm: &mut PageFaultMessage) -> VmFaultReason {
+        PageFaultHandler::filemap_fault(pfm)
+    }
+
+    unsafe fn map_pages(
+        &self,
+        pfm: &mut PageFaultMessage,
+        start_pgoff: usize,
+        end_pgoff: usize,
+    ) -> VmFaultReason {
+        PageFaultHandler::filemap_map_pages(pfm, start_pgoff, end_pgoff)
+    }
+}
+
+impl Ext4FileSystem {
+    pub fn from_gendisk(mount_data: Arc<GenDisk>) -> Result<Arc<dyn FileSystem>, SystemError> {
+        let raw_dev = mount_data.device_num();
+        let fs = another_ext4::Ext4::load(mount_data)?;
+        let root_inode: Arc<LockedExt4Inode> =
+            Arc::new(LockedExt4Inode(SpinLock::new(Ext4Inode {
+                inner_inode_num: another_ext4::EXT4_ROOT_INO,
+                fs_ptr: Weak::default(),
+                page_cache: None,
+                children: BTreeMap::new(),
+                dname: DName::from("/"),
+                vfs_inode_id: generate_inode_id(),
+            })));
+
+        let fs = Arc::new(Ext4FileSystem {
+            fs,
+            raw_dev,
+            root_inode,
+        });
+
+        let mut guard = fs.root_inode.0.lock();
+        guard.fs_ptr = Arc::downgrade(&fs);
+        drop(guard);
+
+        Ok(fs)
+    }
+}
+
+impl MountableFileSystem for Ext4FileSystem {
+    fn make_fs(
+        data: Option<&dyn FileSystemMakerData>,
+    ) -> Result<Arc<dyn FileSystem + 'static>, SystemError> {
+        let mount_data = data
+            .and_then(|d| d.as_any().downcast_ref::<Ext4MountData>())
+            .ok_or(SystemError::EINVAL)?;
+
+        Self::from_gendisk(mount_data.gendisk.clone())
+    }
+    fn make_mount_data(
+        _raw_data: Option<&str>,
+        source: &str,
+    ) -> Result<Option<Arc<dyn FileSystemMakerData + 'static>>, SystemError> {
+        let mount_data = Ext4MountData::from_source(source).map_err(|e| {
+            log::error!(
+                "Failed to create Ext4 mount data from source '{}': {:?}",
+                source,
+                e
+            );
+            e
+        })?;
+        Ok(Some(Arc::new(mount_data)))
+    }
+}
+
+register_mountable_fs!(Ext4FileSystem, EXT4FSMAKER, "ext4");
+
+pub struct Ext4MountData {
+    gendisk: Arc<GenDisk>,
+}
+
+impl FileSystemMakerData for Ext4MountData {
+    fn as_any(&self) -> &dyn core::any::Any {
+        self
+    }
+}
+
+impl Ext4MountData {
+    fn from_source(path: &str) -> Result<Self, SystemError> {
+        let pcb = ProcessManager::current_pcb();
+        let (current_node, rest_path) = user_path_at(&pcb, AtFlags::AT_FDCWD.bits(), path)?;
+        let inode = current_node.lookup_follow_symlink(&rest_path, VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
+        if !inode.metadata()?.file_type.eq(&vfs::FileType::BlockDevice) {
+            return Err(SystemError::ENOTBLK);
+        }
+
+        let disk = inode.dname()?;
+
+        if let Some(gendisk) = try_find_gendisk(disk.0.as_str()) {
+            return Ok(Self { gendisk });
+        }
+        Err(SystemError::ENOENT)
+    }
+}
+
+impl core::fmt::Debug for Ext4FileSystem {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        write!(f, "ext4")
+    }
+}

+ 49 - 0
kernel/src/filesystem/ext4/gendisk.rs

@@ -0,0 +1,49 @@
+use alloc::boxed::Box;
+use kdepends::another_ext4;
+use system_error::SystemError;
+
+use crate::driver::base::block::gendisk::GenDisk;
+
+impl GenDisk {
+    fn convert_from_ext4_blkid(&self, ext4_blkid: u64) -> (usize, usize, usize) {
+        let size = self.block_size_log2();
+        let start_block_offset =
+            ext4_blkid as usize * (another_ext4::BLOCK_SIZE / (1 << size as usize));
+        let lba_id_start = self.block_offset_2_disk_blkid(start_block_offset);
+        let block_count = another_ext4::BLOCK_SIZE / (1 << size as usize);
+        (start_block_offset, lba_id_start, block_count)
+    }
+}
+
+impl another_ext4::BlockDevice for GenDisk {
+    // - convert the ext4 block id to gendisk block id
+    // - read the block from gendisk
+    // - return the block
+    fn read_block(&self, block_id: u64) -> another_ext4::Block {
+        let mut buf: Box<[u8; 4096]> = vec![0u8; another_ext4::BLOCK_SIZE]
+            .into_boxed_slice()
+            .try_into()
+            .expect("Failed to convert boxed slice to boxed array");
+
+        let (_, lba_id_start, block_count) = self.convert_from_ext4_blkid(block_id);
+        self.block_device()
+            .read_at(lba_id_start, block_count, &mut *buf)
+            .map_err(|e| {
+                log::error!("Ext4BlkDevice '{:?}' read_block failed: {:?}", block_id, e);
+                SystemError::EIO
+            })
+            .unwrap();
+        another_ext4::Block::new(block_id, buf)
+    }
+
+    fn write_block(&self, block: &another_ext4::Block) {
+        let (_, lba_id_start, block_count) = self.convert_from_ext4_blkid(block.id);
+        self.block_device()
+            .write_at(lba_id_start, block_count, &*block.data)
+            .map_err(|e| {
+                log::error!("Ext4BlkDevice '{:?}' write_block failed: {:?}", block.id, e);
+                SystemError::EIO
+            })
+            .unwrap();
+    }
+}

+ 389 - 0
kernel/src/filesystem/ext4/inode.rs

@@ -0,0 +1,389 @@
+use crate::{
+    filesystem::{
+        page_cache::PageCache,
+        vfs::{
+            self, syscall::ModeType, utils::DName, vcore::generate_inode_id, FilePrivateData,
+            IndexNode, InodeId,
+        },
+    },
+    libs::spinlock::{SpinLock, SpinLockGuard},
+    time::PosixTimeSpec,
+};
+use alloc::{
+    collections::BTreeMap,
+    string::String,
+    sync::{Arc, Weak},
+    vec::Vec,
+};
+use core::fmt::Debug;
+use kdepends::another_ext4::{self, FileType};
+use num::ToPrimitive;
+use system_error::SystemError;
+
+use super::filesystem::Ext4FileSystem;
+
+type PrivateData<'a> = crate::libs::spinlock::SpinLockGuard<'a, vfs::FilePrivateData>;
+
+pub struct Ext4Inode {
+    // 对应another_ext4里面的inode号,用于在ext4文件系统中查找相应的inode
+    pub(super) inner_inode_num: u32,
+    pub(super) fs_ptr: Weak<super::filesystem::Ext4FileSystem>,
+    pub(super) page_cache: Option<Arc<PageCache>>,
+    pub(super) children: BTreeMap<DName, Arc<LockedExt4Inode>>,
+    pub(super) dname: DName,
+
+    // 对应vfs的inode id,用于标识系统中唯一的inode
+    pub(super) vfs_inode_id: InodeId,
+}
+
+#[derive(Debug)]
+pub struct LockedExt4Inode(pub(super) SpinLock<Ext4Inode>);
+
+impl IndexNode for LockedExt4Inode {
+    fn open(
+        &self,
+        _data: crate::libs::spinlock::SpinLockGuard<vfs::FilePrivateData>,
+        _mode: &vfs::file::FileMode,
+    ) -> Result<(), SystemError> {
+        Ok(())
+    }
+
+    fn create(
+        &self,
+        name: &str,
+        file_type: vfs::FileType,
+        mode: vfs::syscall::ModeType,
+    ) -> Result<Arc<dyn IndexNode>, SystemError> {
+        let guard = self.0.lock();
+        // another_ext4的高4位是文件类型,低12位是权限
+        let file_mode = ModeType::from(file_type).union(mode);
+        let ext4 = &guard.concret_fs().fs;
+        let id = ext4.create(
+            guard.inner_inode_num,
+            name,
+            another_ext4::InodeMode::from_bits_truncate(file_mode.bits() as u16),
+        )?;
+        let inode = LockedExt4Inode::new(id, guard.fs_ptr.clone(), DName::from(name));
+        drop(guard);
+        return Ok(inode as Arc<dyn IndexNode>);
+    }
+
+    fn read_at(
+        &self,
+        offset: usize,
+        len: usize,
+        buf: &mut [u8],
+        data: PrivateData,
+    ) -> Result<usize, SystemError> {
+        let guard = self.0.lock();
+
+        let len = core::cmp::min(len, buf.len());
+        let buf = &mut buf[0..len];
+        let ext4 = &guard.concret_fs().fs;
+        if let Some(page_cache) = &guard.page_cache {
+            let time = PosixTimeSpec::now().tv_sec.to_u32().unwrap_or_else(|| {
+                log::warn!("Failed to get current time, using 0");
+                0
+            });
+            ext4.setattr(
+                guard.inner_inode_num,
+                None,
+                None,
+                None,
+                None,
+                Some(time),
+                None,
+                None,
+                None,
+            )
+            .map_err(SystemError::from)?;
+            page_cache.lock_irqsave().read(offset, buf)
+        } else {
+            self.read_direct(offset, len, buf, data)
+        }
+    }
+
+    fn read_sync(&self, offset: usize, buf: &mut [u8]) -> Result<usize, SystemError> {
+        let guard = self.0.lock();
+        let ext4 = &guard.concret_fs().fs;
+        let inode_num = guard.inner_inode_num;
+        match ext4.getattr(inode_num)?.ftype {
+            FileType::Directory => Err(SystemError::EISDIR),
+            FileType::Unknown => Err(SystemError::EROFS),
+            FileType::RegularFile => ext4.read(inode_num, offset, buf).map_err(From::from),
+            _ => Err(SystemError::EINVAL),
+        }
+    }
+
+    fn read_direct(
+        &self,
+        offset: usize,
+        len: usize,
+        buf: &mut [u8],
+        _data: crate::libs::spinlock::SpinLockGuard<vfs::FilePrivateData>,
+    ) -> Result<usize, SystemError> {
+        let len = core::cmp::min(len, buf.len());
+        self.read_sync(offset, &mut buf[0..len])
+    }
+
+    fn write_at(
+        &self,
+        offset: usize,
+        len: usize,
+        buf: &[u8],
+        data: PrivateData,
+    ) -> Result<usize, SystemError> {
+        let guard = self.0.lock();
+        let ext4 = &guard.concret_fs().fs;
+        let len = core::cmp::min(len, buf.len());
+        let buf = &buf[0..len];
+        if let Some(page_cache) = &guard.page_cache {
+            let write_len = page_cache.lock_irqsave().write(offset, buf)?;
+            let old_file_size = ext4.getattr(guard.inner_inode_num)?.size;
+            let current_file_size = core::cmp::max(old_file_size, (offset + write_len) as u64);
+            let time = PosixTimeSpec::now().tv_sec.to_u32().unwrap_or_else(|| {
+                log::warn!("Failed to get current time, using 0");
+                0
+            });
+            ext4.setattr(
+                guard.inner_inode_num,
+                None,
+                None,
+                None,
+                Some(current_file_size),
+                None,
+                Some(time),
+                None,
+                None,
+            )
+            .map_err(SystemError::from)?;
+            Ok(write_len)
+        } else {
+            self.write_direct(offset, len, buf, data)
+        }
+    }
+
+    fn write_sync(&self, offset: usize, buf: &[u8]) -> Result<usize, SystemError> {
+        let guard = self.0.lock();
+        let ext4 = &guard.concret_fs().fs;
+        let inode_num = guard.inner_inode_num;
+        match ext4.getattr(inode_num)?.ftype {
+            FileType::Directory => Err(SystemError::EISDIR),
+            FileType::Unknown => Err(SystemError::EROFS),
+            FileType::RegularFile => ext4.write(inode_num, offset, buf).map_err(From::from),
+            _ => Err(SystemError::EINVAL),
+        }
+    }
+
+    fn write_direct(
+        &self,
+        offset: usize,
+        len: usize,
+        buf: &[u8],
+        _data: SpinLockGuard<FilePrivateData>,
+    ) -> Result<usize, SystemError> {
+        let len = core::cmp::min(len, buf.len());
+        self.write_sync(offset, &buf[0..len])
+    }
+
+    fn fs(&self) -> Arc<dyn vfs::FileSystem> {
+        self.0.lock().concret_fs()
+    }
+
+    fn as_any_ref(&self) -> &dyn core::any::Any {
+        self
+    }
+
+    fn find(&self, name: &str) -> Result<Arc<dyn IndexNode>, SystemError> {
+        let mut guard = self.0.lock();
+        let dname = DName::from(name);
+        if let Some(child) = guard.children.get(&dname) {
+            return Ok(child.clone() as Arc<dyn IndexNode>);
+        }
+        let next_inode = guard.concret_fs().fs.lookup(guard.inner_inode_num, name)?;
+        let inode = LockedExt4Inode::new(next_inode, guard.fs_ptr.clone(), dname.clone());
+        guard.children.insert(dname, inode.clone());
+        Ok(inode)
+    }
+
+    fn list(&self) -> Result<Vec<String>, SystemError> {
+        let guard = self.0.lock();
+        let dentry = guard.concret_fs().fs.listdir(guard.inner_inode_num)?;
+        let mut list = Vec::new();
+        for entry in dentry {
+            list.push(entry.name());
+        }
+        Ok(list)
+    }
+
+    fn link(&self, name: &str, other: &Arc<dyn IndexNode>) -> Result<(), SystemError> {
+        let guard = self.0.lock();
+        let ext4 = &guard.concret_fs().fs;
+        let inode_num = guard.inner_inode_num;
+        let other = other
+            .downcast_ref::<LockedExt4Inode>()
+            .ok_or(SystemError::EPERM)?;
+
+        let my_attr = ext4.getattr(inode_num)?;
+        let other_attr = ext4.getattr(inode_num)?;
+
+        if my_attr.ftype != another_ext4::FileType::Directory {
+            return Err(SystemError::ENOTDIR);
+        }
+
+        if other_attr.ftype == another_ext4::FileType::Directory {
+            return Err(SystemError::EISDIR);
+        }
+
+        if ext4.lookup(inode_num, name).is_ok() {
+            return Err(SystemError::EEXIST);
+        }
+
+        ext4.link(inode_num, other.0.lock().inner_inode_num, name)?;
+        Ok(())
+    }
+
+    fn unlink(&self, name: &str) -> Result<(), SystemError> {
+        let guard = self.0.lock();
+        let ext4 = &guard.concret_fs().fs;
+        let inode_num = guard.inner_inode_num;
+        let attr = ext4.getattr(inode_num)?;
+        if attr.ftype != another_ext4::FileType::Directory {
+            return Err(SystemError::ENOTDIR);
+        }
+        ext4.unlink(inode_num, name)?;
+        Ok(())
+    }
+
+    fn metadata(&self) -> Result<vfs::Metadata, SystemError> {
+        let guard = self.0.lock();
+        let ext4 = &guard.concret_fs().fs;
+        let attr = ext4.getattr(guard.inner_inode_num)?;
+        let raw_dev = guard.fs_ptr.upgrade().unwrap().raw_dev;
+        Ok(vfs::Metadata {
+            inode_id: guard.vfs_inode_id,
+            size: attr.size as i64,
+            blk_size: another_ext4::BLOCK_SIZE,
+            blocks: attr.blocks as usize,
+            atime: PosixTimeSpec::new(attr.atime.into(), 0),
+            btime: PosixTimeSpec::new(attr.atime.into(), 0),
+            mtime: PosixTimeSpec::new(attr.mtime.into(), 0),
+            ctime: PosixTimeSpec::new(attr.ctime.into(), 0),
+            file_type: Self::file_type(attr.ftype),
+            mode: ModeType::from_bits_truncate(attr.perm.bits() as u32),
+            nlinks: attr.links as usize,
+            uid: attr.uid as usize,
+            gid: attr.gid as usize,
+            dev_id: 0,
+            raw_dev,
+        })
+    }
+
+    fn close(&self, _: PrivateData) -> Result<(), SystemError> {
+        Ok(())
+    }
+
+    fn page_cache(&self) -> Option<Arc<PageCache>> {
+        self.0.lock().page_cache.clone()
+    }
+
+    fn set_metadata(&self, metadata: &vfs::Metadata) -> Result<(), SystemError> {
+        use another_ext4::InodeMode;
+        let mode = metadata.mode.union(ModeType::from(metadata.file_type));
+
+        let to_ext4_time =
+            |time: &PosixTimeSpec| -> u32 { time.tv_sec.max(0).min(u32::MAX as i64) as u32 };
+
+        let guard = self.0.lock();
+        let ext4 = &guard.concret_fs().fs;
+        ext4.setattr(
+            guard.inner_inode_num,
+            Some(InodeMode::from_bits_truncate(mode.bits() as u16)),
+            Some(metadata.uid as u32),
+            Some(metadata.gid as u32),
+            Some(metadata.size as u64),
+            Some(to_ext4_time(&metadata.atime)),
+            Some(to_ext4_time(&metadata.mtime)),
+            Some(to_ext4_time(&metadata.ctime)),
+            Some(to_ext4_time(&metadata.btime)),
+        )?;
+
+        Ok(())
+    }
+
+    fn rmdir(&self, name: &str) -> Result<(), SystemError> {
+        let guard = self.0.lock();
+        let concret_fs = &guard.concret_fs().fs;
+        let inode_num = guard.inner_inode_num;
+        if concret_fs.getattr(inode_num)?.ftype != FileType::Directory {
+            return Err(SystemError::ENOTDIR);
+        }
+        concret_fs.rmdir(inode_num, name)?;
+
+        Ok(())
+    }
+
+    fn dname(&self) -> Result<DName, SystemError> {
+        Ok(self.0.lock().dname.clone())
+    }
+}
+
+impl LockedExt4Inode {
+    pub fn new(
+        inode_num: u32,
+        fs_ptr: Weak<super::filesystem::Ext4FileSystem>,
+        dname: DName,
+    ) -> Arc<Self> {
+        let inode = Arc::new(LockedExt4Inode(SpinLock::new(Ext4Inode::new(
+            inode_num, fs_ptr, dname,
+        ))));
+        let mut guard = inode.0.lock();
+
+        let page_cache = PageCache::new(Some(Arc::downgrade(&inode) as Weak<dyn IndexNode>));
+        guard.page_cache = Some(page_cache);
+
+        drop(guard);
+        return inode;
+    }
+
+    fn file_type(ftype: FileType) -> vfs::FileType {
+        match ftype {
+            FileType::RegularFile => vfs::FileType::File,
+            FileType::Directory => vfs::FileType::Dir,
+            FileType::CharacterDev => vfs::FileType::CharDevice,
+            FileType::BlockDev => vfs::FileType::BlockDevice,
+            FileType::Fifo => vfs::FileType::Pipe,
+            FileType::Socket => vfs::FileType::Socket,
+            FileType::SymLink => vfs::FileType::SymLink,
+            _ => {
+                log::warn!("Unknown file type, going to treat it as a file");
+                vfs::FileType::File
+            }
+        }
+    }
+}
+
+impl Ext4Inode {
+    fn concret_fs(&self) -> Arc<Ext4FileSystem> {
+        self.fs_ptr
+            .upgrade()
+            .expect("Ext4FileSystem should be alive")
+    }
+
+    pub fn new(inode_num: u32, fs_ptr: Weak<Ext4FileSystem>, dname: DName) -> Self {
+        Self {
+            inner_inode_num: inode_num,
+            fs_ptr,
+            page_cache: None,
+            children: BTreeMap::new(),
+            dname,
+            vfs_inode_id: generate_inode_id(),
+        }
+    }
+}
+
+impl Debug for Ext4Inode {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        write!(f, "Ext4Inode")
+    }
+}

+ 4 - 0
kernel/src/filesystem/ext4/mod.rs

@@ -0,0 +1,4 @@
+// 完全不考虑性能的实现
+pub mod filesystem;
+pub mod gendisk;
+pub mod inode;

+ 1 - 0
kernel/src/filesystem/fat/mod.rs

@@ -1,4 +1,5 @@
 pub mod bpb;
 pub mod entry;
 pub mod fs;
+mod mount;
 pub mod utils;

+ 74 - 0
kernel/src/filesystem/fat/mount.rs

@@ -0,0 +1,74 @@
+use crate::filesystem::vfs::FSMAKER;
+use crate::{
+    driver::base::block::gendisk::GenDisk,
+    filesystem::vfs::{
+        self, fcntl::AtFlags, utils::user_path_at, vcore::try_find_gendisk, FileSystem,
+        FileSystemMakerData, MountableFileSystem, VFS_MAX_FOLLOW_SYMLINK_TIMES,
+    },
+    process::ProcessManager,
+    register_mountable_fs,
+};
+use alloc::sync::Arc;
+use system_error::SystemError;
+
+use crate::filesystem::vfs::FileSystemMaker;
+use linkme::distributed_slice;
+
+use super::fs::FATFileSystem;
+
+pub struct FatMountData {
+    gendisk: Arc<GenDisk>,
+}
+
+impl FileSystemMakerData for FatMountData {
+    fn as_any(&self) -> &dyn core::any::Any {
+        self
+    }
+}
+
+impl FatMountData {
+    fn from_source(path: &str) -> Result<Self, SystemError> {
+        let pcb = ProcessManager::current_pcb();
+        let (current_node, rest_path) = user_path_at(&pcb, AtFlags::AT_FDCWD.bits(), path)?;
+        let inode = current_node.lookup_follow_symlink(&rest_path, VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
+        if !inode.metadata()?.file_type.eq(&vfs::FileType::BlockDevice) {
+            return Err(SystemError::ENOTBLK);
+        }
+
+        let disk = inode.dname()?;
+
+        if let Some(gendisk) = try_find_gendisk(disk.0.as_str()) {
+            return Ok(Self { gendisk });
+        }
+        Err(SystemError::ENOENT)
+    }
+}
+
+impl MountableFileSystem for FATFileSystem {
+    fn make_fs(
+        data: Option<&dyn crate::filesystem::vfs::FileSystemMakerData>,
+    ) -> Result<Arc<dyn FileSystem + 'static>, SystemError> {
+        let mount_data = data
+            .and_then(|d| d.as_any().downcast_ref::<FatMountData>())
+            .ok_or(SystemError::EINVAL)?;
+
+        let fs = Self::new(mount_data.gendisk.clone())?;
+        Ok(fs)
+    }
+    fn make_mount_data(
+        _raw_data: Option<&str>,
+        source: &str,
+    ) -> Result<Option<Arc<dyn FileSystemMakerData + 'static>>, SystemError> {
+        let mount_data = FatMountData::from_source(source).map_err(|e| {
+            log::error!(
+                "Failed to create FAT mount data from source '{}': {:?}",
+                source,
+                e
+            );
+            e
+        })?;
+        Ok(Some(Arc::new(mount_data)))
+    }
+}
+
+register_mountable_fs!(FATFileSystem, FATFSMAKER, "vfat");

+ 1 - 0
kernel/src/filesystem/mod.rs

@@ -2,6 +2,7 @@ pub mod devfs;
 pub mod devpts;
 pub mod epoll;
 pub mod eventfd;
+pub mod ext4;
 pub mod fat;
 pub mod fs;
 pub mod kernfs;

+ 24 - 17
kernel/src/filesystem/overlayfs/mod.rs

@@ -3,12 +3,15 @@ pub mod copy_up;
 pub mod entry;
 
 use super::ramfs::{LockedRamFSInode, RamFSInode};
-use super::vfs::{self, FileSystem, FileType, FsInfo, IndexNode, Metadata, SuperBlock};
+use super::vfs::{
+    self, FileSystem, FileType, FsInfo, IndexNode, Metadata, MountableFileSystem, SuperBlock,
+};
 use super::vfs::{FSMAKER, ROOT_INODE};
 use crate::driver::base::device::device_number::DeviceNumber;
 use crate::driver::base::device::device_number::Major;
 use crate::filesystem::vfs::{FileSystemMaker, FileSystemMakerData};
 use crate::libs::spinlock::SpinLock;
+use crate::register_mountable_fs;
 use alloc::string::String;
 use alloc::sync::Arc;
 use alloc::sync::Weak;
@@ -21,14 +24,6 @@ const WHITEOUT_MODE: u64 = 0o020000 | 0o600; // whiteout字符设备文件模式
 const WHITEOUT_DEV: DeviceNumber = DeviceNumber::new(Major::UNNAMED_MAJOR, 0); // Whiteout 文件设备号
 const WHITEOUT_FLAG: u64 = 0x1;
 
-#[distributed_slice(FSMAKER)]
-static OVERLAYFSMAKER: FileSystemMaker = FileSystemMaker::new(
-    "overlay",
-    &(OverlayFS::make_overlayfs
-        as fn(
-            Option<&dyn FileSystemMakerData>,
-        ) -> Result<Arc<dyn FileSystem + 'static>, SystemError>),
-);
 #[derive(Debug)]
 pub struct OverlayMountData {
     upper_dir: String,
@@ -37,15 +32,11 @@ pub struct OverlayMountData {
 }
 
 impl OverlayMountData {
-    pub fn from_row(raw_data: *const u8) -> Result<Self, SystemError> {
-        if raw_data.is_null() {
+    pub fn from_raw(raw_data: Option<&str>) -> Result<Self, SystemError> {
+        if raw_data.is_none() {
             return Err(SystemError::EINVAL);
         }
-        let len = (0..)
-            .find(|&i| unsafe { raw_data.add(i).read() } == 0)
-            .ok_or(SystemError::EINVAL)?;
-        let slice = unsafe { core::slice::from_raw_parts(raw_data, len) };
-        let raw_str = core::str::from_utf8(slice).map_err(|_| SystemError::EINVAL)?;
+        let raw_str = raw_data.unwrap();
         let mut data = OverlayMountData {
             upper_dir: String::new(),
             lower_dirs: Vec::new(),
@@ -146,7 +137,10 @@ impl OverlayFS {
     pub fn ovl_upper_mnt(&self) -> Arc<dyn IndexNode> {
         self.layers[0].mnt.clone()
     }
-    pub fn make_overlayfs(
+}
+
+impl MountableFileSystem for OverlayFS {
+    fn make_fs(
         data: Option<&dyn FileSystemMakerData>,
     ) -> Result<Arc<dyn FileSystem + 'static>, SystemError> {
         let mount_data = data
@@ -204,8 +198,21 @@ impl OverlayFS {
         };
         Ok(Arc::new(fs))
     }
+
+    fn make_mount_data(
+        raw_data: Option<&str>,
+        _source: &str,
+    ) -> Result<Option<Arc<dyn FileSystemMakerData + 'static>>, SystemError> {
+        let mount_data = OverlayMountData::from_raw(raw_data).map_err(|e| {
+            log::error!("Failed to create overlay mount data: {:?}", e);
+            e
+        })?;
+        Ok(Some(Arc::new(mount_data)))
+    }
 }
 
+register_mountable_fs!(OverlayFS, OVERLAYFSMAKER, "overlay");
+
 impl OvlInode {
     pub fn ovl_lower_redirect(&self) -> Option<&str> {
         if self.file_type == FileType::File || self.file_type == FileType::Dir {

+ 14 - 10
kernel/src/filesystem/ramfs/mod.rs

@@ -3,6 +3,7 @@ use core::intrinsics::unlikely;
 
 use crate::filesystem::vfs::{FileSystemMakerData, FSMAKER};
 use crate::libs::rwlock::RwLock;
+use crate::register_mountable_fs;
 use crate::{
     driver::base::device::device_number::DeviceNumber,
     filesystem::vfs::{vcore::generate_inode_id, FileType},
@@ -28,7 +29,7 @@ use super::vfs::{
 
 use linkme::distributed_slice;
 
-use super::vfs::{Magic, SuperBlock};
+use super::vfs::{Magic, MountableFileSystem, SuperBlock};
 
 /// RamFS的inode名称的最大长度
 const RAMFS_MAX_NAMELEN: usize = 64;
@@ -153,22 +154,25 @@ impl RamFS {
 
         return result;
     }
+}
 
-    pub fn make_ramfs(
+impl MountableFileSystem for RamFS {
+    fn make_mount_data(
+        _raw_data: Option<&str>,
+        _source: &str,
+    ) -> Result<Option<Arc<dyn FileSystemMakerData + 'static>>, SystemError> {
+        // 目前ramfs不需要任何额外的mount数据
+        Ok(None)
+    }
+    fn make_fs(
         _data: Option<&dyn FileSystemMakerData>,
     ) -> Result<Arc<dyn FileSystem + 'static>, SystemError> {
         let fs = RamFS::new();
         return Ok(fs);
     }
 }
-#[distributed_slice(FSMAKER)]
-static RAMFSMAKER: FileSystemMaker = FileSystemMaker::new(
-    "ramfs",
-    &(RamFS::make_ramfs
-        as fn(
-            Option<&dyn FileSystemMakerData>,
-        ) -> Result<Arc<dyn FileSystem + 'static>, SystemError>),
-);
+
+register_mountable_fs!(RamFS, RAMFSMAKER, "ramfs");
 
 impl IndexNode for LockedRamFSInode {
     fn truncate(&self, len: usize) -> Result<(), SystemError> {

+ 109 - 26
kernel/src/filesystem/vfs/mod.rs

@@ -921,6 +921,7 @@ bitflags! {
     pub struct Magic: u64 {
         const DEVFS_MAGIC = 0x1373;
         const FAT_MAGIC =  0xf2f52011;
+        const EXT4_MAGIC = 0xef53;
         const KER_MAGIC = 0x3153464b;
         const PROC_MAGIC = 0x9fa0;
         const RAMFS_MAGIC = 0x858458f6;
@@ -970,6 +971,67 @@ impl DowncastArc for dyn FileSystem {
     }
 }
 
+/// # 可以被挂载的文件系统应该实现的trait
+pub trait MountableFileSystem: FileSystem {
+    fn make_mount_data(
+        _raw_data: Option<&str>,
+        _source: &str,
+    ) -> Result<Option<Arc<dyn FileSystemMakerData + 'static>>, SystemError> {
+        log::error!("This filesystem does not support make_mount_data");
+        Err(SystemError::ENOSYS)
+    }
+
+    fn make_fs(
+        _data: Option<&dyn FileSystemMakerData>,
+    ) -> Result<Arc<dyn FileSystem + 'static>, SystemError> {
+        log::error!("This filesystem does not support make_fs");
+        Err(SystemError::ENOSYS)
+    }
+}
+
+/// # 注册一个可以被挂载文件系统
+/// 此宏用于注册一个可以被挂载的文件系统。
+/// 它会将文件系统的创建函数和挂载数据创建函数注册到全局的`FSMAKER`数组中。
+///
+/// ## 参数
+/// - `$fs`: 文件系统对应的结构体
+/// - `$maker_name`: 文件系统的注册名
+/// - `$fs_name`: 文件系统的名称(字符串字面量)
+#[macro_export]
+macro_rules! register_mountable_fs {
+    ($fs:ident, $maker_name:ident, $fs_name:literal) => {
+        impl $fs {
+            fn make_fs_bridge(
+                data: Option<&dyn FileSystemMakerData>,
+            ) -> Result<Arc<dyn FileSystem>, SystemError> {
+                <$fs as MountableFileSystem>::make_fs(data)
+            }
+
+            fn make_mount_data_bridge(
+                raw_data: Option<&str>,
+                source: &str,
+            ) -> Result<Option<Arc<dyn FileSystemMakerData + 'static>>, SystemError> {
+                <$fs as MountableFileSystem>::make_mount_data(raw_data, source)
+            }
+        }
+
+        #[distributed_slice(FSMAKER)]
+        static $maker_name: FileSystemMaker = FileSystemMaker::new(
+            $fs_name,
+            &($fs::make_fs_bridge
+                as fn(
+                    Option<&dyn FileSystemMakerData>,
+                ) -> Result<Arc<dyn FileSystem + 'static>, SystemError>),
+            &($fs::make_mount_data_bridge
+                as fn(
+                    Option<&str>,
+                    &str,
+                )
+                    -> Result<Option<Arc<dyn FileSystemMakerData + 'static>>, SystemError>),
+        );
+    };
+}
+
 #[derive(Debug)]
 pub struct FsInfo {
     /// 文件系统所在的块设备的id
@@ -1011,23 +1073,32 @@ impl Metadata {
     }
 }
 pub struct FileSystemMaker {
-    function: &'static FileSystemNewFunction,
+    /// 文件系统的创建函数
+    maker: &'static FSMakerFunction,
+    /// 文件系统的名称
     name: &'static str,
+    /// 用于创建挂载数据的函数
+    builder: &'static MountDataBuilder,
 }
 
 impl FileSystemMaker {
     pub const fn new(
         name: &'static str,
-        function: &'static FileSystemNewFunction,
+        maker: &'static FSMakerFunction,
+        builder: &'static MountDataBuilder,
     ) -> FileSystemMaker {
-        FileSystemMaker { function, name }
+        FileSystemMaker {
+            maker,
+            name,
+            builder,
+        }
     }
 
-    pub fn call(
+    pub fn build(
         &self,
         data: Option<&dyn FileSystemMakerData>,
     ) -> Result<Arc<dyn FileSystem>, SystemError> {
-        (self.function)(data)
+        (self.maker)(data)
     }
 }
 
@@ -1035,8 +1106,13 @@ pub trait FileSystemMakerData: Send + Sync {
     fn as_any(&self) -> &dyn Any;
 }
 
-pub type FileSystemNewFunction =
+pub type FSMakerFunction =
     fn(data: Option<&dyn FileSystemMakerData>) -> Result<Arc<dyn FileSystem>, SystemError>;
+pub type MountDataBuilder =
+    fn(
+        raw_data: Option<&str>,
+        source: &str,
+    ) -> Result<Option<Arc<dyn FileSystemMakerData + 'static>>, SystemError>;
 
 #[macro_export]
 macro_rules! define_filesystem_maker_slice {
@@ -1049,27 +1125,34 @@ macro_rules! define_filesystem_maker_slice {
     };
 }
 
-/// 调用指定数组中的所有初始化器
-#[macro_export]
-macro_rules! producefs {
-    ($initializer_slice:ident,$filesystem:ident,$raw_data : ident) => {
-        match $initializer_slice.iter().find(|&m| m.name == $filesystem) {
-            Some(maker) => {
-                let mount_data = match $filesystem {
-                    "overlay" => OverlayMountData::from_row($raw_data).ok(),
-                    _ => None,
-                };
-                let data: Option<&dyn FileSystemMakerData> =
-                    mount_data.as_ref().map(|d| d as &dyn FileSystemMakerData);
-
-                maker.call(data)
-            }
-            None => {
-                log::error!("mismatch filesystem type : {}", $filesystem);
-                Err(SystemError::EINVAL)
-            }
+/// # 通过文件系统的名称和数据创建一个文件系统实例
+///
+/// ## 参数
+/// - `filesystem`: 文件系统的名称
+/// - `data`: 可选的挂载数据
+/// - `source`: 挂载源
+///
+/// ## 返回值
+/// - `Ok(Arc<dyn FileSystem>)`: 成功时返回文件系统的共享引用
+/// - `Err(SystemError)`: 如果找不到对应的文件系统或创建失败,则返回错误
+///
+/// 这个是之前的`produce_fs!`的函数版本,改成了函数之后ext4的挂载会慢一点,仅作记录
+pub fn produce_fs(
+    filesystem: &str,
+    data: Option<&str>,
+    source: &str,
+) -> Result<Arc<dyn FileSystem>, SystemError> {
+    match FSMAKER.iter().find(|&m| m.name == filesystem) {
+        Some(maker) => {
+            let mount_data = (maker.builder)(data, source)?;
+            let mount_data_ref = mount_data.as_ref().map(|arc| arc.as_ref());
+            maker.build(mount_data_ref)
         }
-    };
+        None => {
+            log::error!("mismatch filesystem type : {}", filesystem);
+            Err(SystemError::EINVAL)
+        }
+    }
 }
 
 define_filesystem_maker_slice!(FSMAKER);

+ 35 - 1
kernel/src/filesystem/vfs/mount.rs

@@ -14,7 +14,10 @@ use system_error::SystemError;
 
 use crate::{
     driver::base::device::device_number::DeviceNumber,
-    filesystem::{page_cache::PageCache, vfs::ROOT_INODE},
+    filesystem::{
+        page_cache::PageCache,
+        vfs::{fcntl::AtFlags, vcore::do_mkdir_at, ROOT_INODE},
+    },
     libs::{
         casting::DowncastArc,
         rwlock::RwLock,
@@ -764,3 +767,34 @@ pub fn is_mountpoint_root(inode: &Arc<dyn IndexNode>) -> bool {
 
     return false;
 }
+
+/// # do_mount_mkdir - 在指定挂载点创建目录并挂载文件系统
+///
+/// 在指定的挂载点创建一个目录,并将其挂载到文件系统中。如果挂载点已经存在,并且不是空的,
+/// 则会返回错误。成功时,会返回一个新的挂载文件系统的引用。
+///
+/// ## 参数
+///
+/// - `fs`: FileSystem - 文件系统的引用,用于创建和挂载目录。
+/// - `mount_point`: &str - 挂载点路径,用于创建和挂载目录。
+///
+/// ## 返回值
+///
+/// - `Ok(Arc<MountFS>)`: 成功挂载文件系统后,返回挂载文件系统的共享引用。
+/// - `Err(SystemError)`: 挂载失败时,返回系统错误。
+pub fn do_mount_mkdir(
+    fs: Arc<dyn FileSystem>,
+    mount_point: &str,
+) -> Result<Arc<MountFS>, SystemError> {
+    let inode = do_mkdir_at(
+        AtFlags::AT_FDCWD.bits(),
+        mount_point,
+        FileMode::from_bits_truncate(0o755),
+    )?;
+    if let Some((_, rest, _fs)) = MOUNT_LIST().get_mount_point(mount_point) {
+        if rest.is_empty() {
+            return Err(SystemError::EBUSY);
+        }
+    }
+    return inode.mount(fs);
+}

+ 11 - 85
kernel/src/filesystem/vfs/syscall/mod.rs

@@ -1,5 +1,4 @@
-use crate::filesystem::overlayfs::OverlayMountData;
-use crate::filesystem::vfs::{FileSystemMakerData, FilldirContext};
+use crate::filesystem::vfs::FilldirContext;
 use core::mem::size_of;
 
 use alloc::{string::String, sync::Arc, vec::Vec};
@@ -7,11 +6,10 @@ use alloc::{string::String, sync::Arc, vec::Vec};
 use log::warn;
 use system_error::SystemError;
 
-use crate::producefs;
 use crate::syscall::user_access::UserBufferReader;
 use crate::{
     driver::base::{block::SeekFrom, device::device_number::DeviceNumber},
-    filesystem::vfs::{file::FileDescriptorVec, vcore as Vcore},
+    filesystem::vfs::file::FileDescriptorVec,
     libs::rwlock::RwLockWriteGuard,
     process::ProcessManager,
     syscall::{
@@ -22,7 +20,6 @@ use crate::{
 };
 
 use super::stat::{do_newfstatat, do_statx, vfs_fstat};
-use super::vcore::do_symlinkat;
 use super::{
     fcntl::{AtFlags, FcntlCommand, FD_CLOEXEC},
     file::{File, FileMode},
@@ -31,8 +28,7 @@ use super::{
     },
     utils::{rsplit_path, user_path_at},
     vcore::{do_mkdir_at, do_remove_dir, do_unlink_at},
-    FileType, IndexNode, SuperBlock, FSMAKER, MAX_PATHLEN, ROOT_INODE,
-    VFS_MAX_FOLLOW_SYMLINK_TIMES,
+    FileType, IndexNode, SuperBlock, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES,
 };
 
 mod open_utils;
@@ -60,6 +56,14 @@ mod sys_epoll_pwait;
 #[cfg(target_arch = "x86_64")]
 mod sys_epoll_wait;
 
+pub mod sys_mount;
+pub mod sys_umount2;
+
+pub mod symlink_utils;
+#[cfg(target_arch = "x86_64")]
+mod sys_symlink;
+mod sys_symlinkat;
+
 pub const SEEK_SET: u32 = 0;
 pub const SEEK_CUR: u32 = 1;
 pub const SEEK_END: u32 = 2;
@@ -434,16 +438,6 @@ bitflags! {
     }
 }
 
-bitflags! {
-    pub struct UmountFlag: i32 {
-        const DEFAULT = 0;          /* Default call to umount. */
-        const MNT_FORCE = 1;        /* Force unmounting.  */
-        const MNT_DETACH = 2;       /* Just detach from the tree.  */
-        const MNT_EXPIRE = 4;       /* Mark for expiry.  */
-        const UMOUNT_NOFOLLOW = 8;  /* Don't follow symlink on umount.  */
-    }
-}
-
 impl Syscall {
     pub fn openat(
         dirfd: i32,
@@ -880,18 +874,6 @@ impl Syscall {
         return do_unlink_at(AtFlags::AT_FDCWD.bits(), &path).map(|v| v as usize);
     }
 
-    pub fn symlink(oldname: *const u8, newname: *const u8) -> Result<usize, SystemError> {
-        return do_symlinkat(oldname, AtFlags::AT_FDCWD.bits(), newname);
-    }
-
-    pub fn symlinkat(
-        oldname: *const u8,
-        newdfd: i32,
-        newname: *const u8,
-    ) -> Result<usize, SystemError> {
-        return do_symlinkat(oldname, newdfd, newname);
-    }
-
     /// # 修改文件名
     ///
     ///
@@ -1421,62 +1403,6 @@ impl Syscall {
         return ksys_fchown(fd, uid, gid);
     }
 
-    /// #挂载文件系统
-    ///
-    /// 用于挂载文件系统,目前仅支持ramfs挂载
-    ///
-    /// ## 参数:
-    ///
-    /// - source       挂载设备(暂时不支持)
-    /// - target       挂载目录
-    /// - filesystemtype   文件系统
-    /// - mountflags     挂载选项(暂未实现)
-    /// - data        带数据挂载
-    ///
-    /// ## 返回值
-    /// - Ok(0): 挂载成功
-    /// - Err(SystemError) :挂载过程中出错
-    pub fn mount(
-        _source: *const u8,
-        target: *const u8,
-        filesystemtype: *const u8,
-        _mountflags: usize,
-        data: *const u8,
-    ) -> Result<usize, SystemError> {
-        let target = user_access::check_and_clone_cstr(target, Some(MAX_PATHLEN))?
-            .into_string()
-            .map_err(|_| SystemError::EINVAL)?;
-
-        let fstype_str = user_access::check_and_clone_cstr(filesystemtype, Some(MAX_PATHLEN))?;
-        let fstype_str = fstype_str.to_str().map_err(|_| SystemError::EINVAL)?;
-
-        let fstype = producefs!(FSMAKER, fstype_str, data)?;
-
-        Vcore::do_mount(fstype, &target)?;
-
-        return Ok(0);
-    }
-
-    // 想法:可以在VFS中实现一个文件系统分发器,流程如下:
-    // 1. 接受从上方传来的文件类型字符串
-    // 2. 将传入值与启动时准备好的字符串数组逐个比较(probe)
-    // 3. 直接在函数内调用构造方法并直接返回文件系统对象
-
-    /// src/linux/mount.c `umount` & `umount2`
-    ///
-    /// [umount(2) — Linux manual page](https://www.man7.org/linux/man-pages/man2/umount.2.html)
-    pub fn umount2(target: *const u8, flags: i32) -> Result<(), SystemError> {
-        let target = user_access::check_and_clone_cstr(target, Some(MAX_PATHLEN))?
-            .into_string()
-            .map_err(|_| SystemError::EINVAL)?;
-        Vcore::do_umount2(
-            AtFlags::AT_FDCWD.bits(),
-            &target,
-            UmountFlag::from_bits(flags).ok_or(SystemError::EINVAL)?,
-        )?;
-        return Ok(());
-    }
-
     pub fn sys_utimensat(
         dirfd: i32,
         pathname: *const u8,

+ 50 - 0
kernel/src/filesystem/vfs/syscall/symlink_utils.rs

@@ -0,0 +1,50 @@
+use system_error::SystemError;
+
+use crate::{
+    filesystem::vfs::{
+        fcntl::AtFlags,
+        utils::{rsplit_path, user_path_at},
+        FilePrivateData, FileType, VFS_MAX_FOLLOW_SYMLINK_TIMES,
+    },
+    libs::spinlock::SpinLock,
+    process::ProcessManager,
+};
+
+use super::ModeType;
+
+pub fn do_symlinkat(from: &str, newdfd: Option<i32>, to: &str) -> Result<usize, SystemError> {
+    let newdfd = match newdfd {
+        Some(fd) => fd,
+        None => AtFlags::AT_FDCWD.bits(),
+    };
+
+    // TODO: 添加权限检查,确保进程拥有目标路径的权限
+    let pcb = ProcessManager::current_pcb();
+    let (old_begin_inode, old_remain_path) = user_path_at(&pcb, AtFlags::AT_FDCWD.bits(), from)?;
+    // info!("old_begin_inode={:?}", old_begin_inode.metadata());
+    let _ =
+        old_begin_inode.lookup_follow_symlink(&old_remain_path, VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
+
+    // 得到新创建节点的父节点
+    let (new_begin_inode, new_remain_path) = user_path_at(&pcb, newdfd, to)?;
+    let (new_name, new_parent_path) = rsplit_path(&new_remain_path);
+    let new_parent = new_begin_inode
+        .lookup_follow_symlink(new_parent_path.unwrap_or("/"), VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
+    // info!("new_parent={:?}", new_parent.metadata());
+
+    if new_parent.metadata()?.file_type != FileType::Dir {
+        return Err(SystemError::ENOTDIR);
+    }
+
+    let new_inode = new_parent.create_with_data(
+        new_name,
+        FileType::SymLink,
+        ModeType::from_bits_truncate(0o777),
+        0,
+    )?;
+
+    let buf = old_remain_path.as_bytes();
+    let len = buf.len();
+    new_inode.write_at(0, len, buf, SpinLock::new(FilePrivateData::Unused).lock())?;
+    return Ok(0);
+}

+ 137 - 0
kernel/src/filesystem/vfs/syscall/sys_mount.rs

@@ -0,0 +1,137 @@
+//! System call handler for sys_mount.
+
+use crate::{
+    arch::{interrupt::TrapFrame, syscall::nr::SYS_MOUNT},
+    filesystem::vfs::{
+        fcntl::AtFlags, mount::MOUNT_LIST, produce_fs, utils::user_path_at, FileSystem, MountFS,
+        MAX_PATHLEN, VFS_MAX_FOLLOW_SYMLINK_TIMES,
+    },
+    process::ProcessManager,
+    syscall::{
+        table::{FormattedSyscallParam, Syscall},
+        user_access,
+    },
+};
+use alloc::sync::Arc;
+use alloc::vec::Vec;
+use system_error::SystemError;
+
+/// #挂载文件系统
+///
+/// 用于挂载文件系统,目前仅支持ramfs挂载
+///
+/// ## 参数:
+///
+/// - source       挂载设备(目前只支持ext4格式的硬盘)
+/// - target       挂载目录
+/// - filesystemtype   文件系统
+/// - mountflags     挂载选项(暂未实现)
+/// - data        带数据挂载
+///
+/// ## 返回值
+/// - Ok(0): 挂载成功
+/// - Err(SystemError) :挂载过程中出错
+pub struct SysMountHandle;
+
+impl Syscall for SysMountHandle {
+    fn num_args(&self) -> usize {
+        5
+    }
+
+    fn handle(&self, args: &[usize], _frame: &mut TrapFrame) -> Result<usize, SystemError> {
+        let target = Self::target(args);
+        let filesystemtype = Self::filesystemtype(args);
+        let data = Self::raw_data(args);
+        let source = Self::source(args);
+
+        let target = user_access::check_and_clone_cstr(target, Some(MAX_PATHLEN))?
+            .into_string()
+            .map_err(|_| SystemError::EINVAL)?;
+        let source = user_access::check_and_clone_cstr(source, Some(MAX_PATHLEN))?
+            .into_string()
+            .map_err(|_| SystemError::EINVAL)?;
+        let source = source.as_str();
+
+        let fstype_str = user_access::check_and_clone_cstr(filesystemtype, Some(MAX_PATHLEN))?;
+        let fstype_str = fstype_str.to_str().map_err(|_| SystemError::EINVAL)?;
+
+        let fs = produce_fs(fstype_str, data, source)?;
+
+        do_mount(fs, &target)?;
+
+        return Ok(0);
+    }
+
+    fn entry_format(&self, args: &[usize]) -> Vec<FormattedSyscallParam> {
+        vec![
+            FormattedSyscallParam::new("source", format!("{:#x}", Self::source(args) as usize)),
+            FormattedSyscallParam::new("target", format!("{:#x}", Self::target(args) as usize)),
+            FormattedSyscallParam::new(
+                "filesystem type",
+                format!("{:#x}", Self::filesystemtype(args) as usize),
+            ),
+            FormattedSyscallParam::new("mountflags", format!("{:#x}", Self::mountflags(args))),
+            FormattedSyscallParam::new("data", format!("{:?}", Self::raw_data(args))),
+        ]
+    }
+}
+
+impl SysMountHandle {
+    fn source(args: &[usize]) -> *const u8 {
+        args[0] as *const u8
+    }
+    fn target(args: &[usize]) -> *const u8 {
+        args[1] as *const u8
+    }
+    fn filesystemtype(args: &[usize]) -> *const u8 {
+        args[2] as *const u8
+    }
+    fn mountflags(args: &[usize]) -> usize {
+        args[3]
+    }
+    fn raw_data(args: &[usize]) -> Option<&'static str> {
+        let raw = args[4] as *const u8;
+        if raw.is_null() {
+            return None;
+        }
+        let len = (0..).find(|&i| unsafe { raw.add(i).read() } == 0).unwrap();
+
+        let slice = unsafe { core::slice::from_raw_parts(raw, len) };
+        let raw_str = core::str::from_utf8(slice).ok().unwrap();
+        Some(raw_str)
+    }
+}
+
+syscall_table_macros::declare_syscall!(SYS_MOUNT, SysMountHandle);
+
+/// # do_mount - 挂载文件系统
+///
+/// 将给定的文件系统挂载到指定的挂载点。
+///
+/// 此函数会检查是否已经挂载了相同的文件系统,如果已经挂载,则返回错误。
+/// 它还会处理符号链接,并确保挂载点是有效的。
+///
+/// ## 参数
+///
+/// - `fs`: Arc<dyn FileSystem>,要挂载的文件系统。
+/// - `mount_point`: &str,挂载点路径。
+///
+/// ## 返回值
+///
+/// - `Ok(Arc<MountFS>)`: 挂载成功后返回挂载的文件系统。
+/// - `Err(SystemError)`: 挂载失败时返回错误。
+pub fn do_mount(fs: Arc<dyn FileSystem>, mount_point: &str) -> Result<Arc<MountFS>, SystemError> {
+    let (current_node, rest_path) = user_path_at(
+        &ProcessManager::current_pcb(),
+        AtFlags::AT_FDCWD.bits(),
+        mount_point,
+    )?;
+    let inode = current_node.lookup_follow_symlink(&rest_path, VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
+    if let Some((_, rest, _fs)) = MOUNT_LIST().get_mount_point(mount_point) {
+        if rest.is_empty() {
+            return Err(SystemError::EBUSY);
+        }
+    }
+    // 移至IndexNode.mount()来记录
+    return inode.mount(fs);
+}

+ 61 - 0
kernel/src/filesystem/vfs/syscall/sys_symlink.rs

@@ -0,0 +1,61 @@
+//! System call handler for sys_symlink.
+
+use crate::{
+    arch::{interrupt::TrapFrame, syscall::nr::SYS_SYMLINK},
+    filesystem::vfs::MAX_PATHLEN,
+    syscall::{
+        table::{FormattedSyscallParam, Syscall},
+        user_access::check_and_clone_cstr,
+    },
+};
+use alloc::string::{String, ToString};
+use alloc::vec::Vec;
+use system_error::SystemError;
+
+use super::symlink_utils::do_symlinkat;
+
+pub struct SysSymlinkHandle;
+
+impl Syscall for SysSymlinkHandle {
+    fn num_args(&self) -> usize {
+        2
+    }
+
+    fn handle(&self, args: &[usize], _frame: &mut TrapFrame) -> Result<usize, SystemError> {
+        let from = Self::from(args);
+        let to = Self::to(args);
+
+        do_symlinkat(from.as_str(), None, to.as_str())
+    }
+
+    fn entry_format(&self, args: &[usize]) -> Vec<FormattedSyscallParam> {
+        vec![
+            FormattedSyscallParam::new("from", Self::from(args)),
+            FormattedSyscallParam::new("to", Self::to(args)),
+        ]
+    }
+}
+
+impl SysSymlinkHandle {
+    fn from(args: &[usize]) -> String {
+        check_and_clone_cstr(args[0] as *const u8, Some(MAX_PATHLEN))
+            .unwrap()
+            .into_string()
+            .map_err(|_| SystemError::EINVAL)
+            .unwrap()
+            .trim()
+            .to_string()
+    }
+
+    fn to(args: &[usize]) -> String {
+        check_and_clone_cstr(args[1] as *const u8, Some(MAX_PATHLEN))
+            .unwrap()
+            .into_string()
+            .map_err(|_| SystemError::EINVAL)
+            .unwrap()
+            .trim()
+            .to_string()
+    }
+}
+
+syscall_table_macros::declare_syscall!(SYS_SYMLINK, SysSymlinkHandle);

+ 67 - 0
kernel/src/filesystem/vfs/syscall/sys_symlinkat.rs

@@ -0,0 +1,67 @@
+//! System call handler for sys_symlinkat.
+
+use crate::{
+    arch::{interrupt::TrapFrame, syscall::nr::SYS_SYMLINKAT},
+    filesystem::vfs::MAX_PATHLEN,
+    syscall::{
+        table::{FormattedSyscallParam, Syscall},
+        user_access::check_and_clone_cstr,
+    },
+};
+use alloc::string::{String, ToString};
+use alloc::vec::Vec;
+use system_error::SystemError;
+
+use super::symlink_utils::do_symlinkat;
+
+pub struct SysSymlinkAtHandle;
+
+impl Syscall for SysSymlinkAtHandle {
+    fn num_args(&self) -> usize {
+        3
+    }
+
+    fn handle(&self, args: &[usize], _frame: &mut TrapFrame) -> Result<usize, SystemError> {
+        let from = Self::from(args);
+        let to = Self::to(args);
+        let newdfd = Self::newdfd(args);
+
+        do_symlinkat(from.as_str(), Some(newdfd), to.as_str())
+    }
+
+    fn entry_format(&self, args: &[usize]) -> Vec<FormattedSyscallParam> {
+        vec![
+            FormattedSyscallParam::new("from", Self::from(args)),
+            FormattedSyscallParam::new("newdfd", Self::newdfd(args).to_string()),
+            FormattedSyscallParam::new("to", Self::to(args)),
+        ]
+    }
+}
+
+impl SysSymlinkAtHandle {
+    fn from(args: &[usize]) -> String {
+        check_and_clone_cstr(args[0] as *const u8, Some(MAX_PATHLEN))
+            .unwrap()
+            .into_string()
+            .map_err(|_| SystemError::EINVAL)
+            .unwrap()
+            .trim()
+            .to_string()
+    }
+
+    fn newdfd(args: &[usize]) -> i32 {
+        args[1] as i32
+    }
+
+    fn to(args: &[usize]) -> String {
+        check_and_clone_cstr(args[2] as *const u8, Some(MAX_PATHLEN))
+            .unwrap()
+            .into_string()
+            .map_err(|_| SystemError::EINVAL)
+            .unwrap()
+            .trim()
+            .to_string()
+    }
+}
+
+syscall_table_macros::declare_syscall!(SYS_SYMLINKAT, SysSymlinkAtHandle);

+ 104 - 0
kernel/src/filesystem/vfs/syscall/sys_umount2.rs

@@ -0,0 +1,104 @@
+//! System call handler for sys_umount.
+
+use crate::{
+    arch::{interrupt::TrapFrame, syscall::nr::SYS_UMOUNT2},
+    filesystem::vfs::{
+        fcntl::AtFlags, mount::MOUNT_LIST, utils::user_path_at, MountFS, MAX_PATHLEN,
+    },
+    process::ProcessManager,
+    syscall::{
+        table::{FormattedSyscallParam, Syscall},
+        user_access,
+    },
+};
+use alloc::{sync::Arc, vec::Vec};
+use system_error::SystemError;
+
+/// src/linux/mount.c `umount` & `umount2`
+///
+/// [umount(2) — Linux manual page](https://www.man7.org/linux/man-pages/man2/umount.2.html)
+pub struct SysUmount2Handle;
+
+impl Syscall for SysUmount2Handle {
+    fn num_args(&self) -> usize {
+        2
+    }
+
+    fn handle(&self, args: &[usize], _frame: &mut TrapFrame) -> Result<usize, SystemError> {
+        let target = Self::target(args);
+        let flags = Self::flags(args);
+
+        let target = user_access::check_and_clone_cstr(target, Some(MAX_PATHLEN))?
+            .into_string()
+            .map_err(|_| SystemError::EINVAL)?;
+        do_umount2(
+            AtFlags::AT_FDCWD.bits(),
+            &target,
+            UmountFlag::from_bits(flags).ok_or(SystemError::EINVAL)?,
+        )?;
+        return Ok(0);
+    }
+
+    fn entry_format(&self, args: &[usize]) -> Vec<FormattedSyscallParam> {
+        vec![
+            FormattedSyscallParam::new("target", format!("{:#x}", Self::target(args) as usize)),
+            FormattedSyscallParam::new("flags", format!("{:#x}", Self::flags(args))),
+        ]
+    }
+}
+
+impl SysUmount2Handle {
+    fn target(args: &[usize]) -> *const u8 {
+        args[0] as *const u8
+    }
+
+    fn flags(args: &[usize]) -> i32 {
+        args[1] as i32
+    }
+}
+
+syscall_table_macros::declare_syscall!(SYS_UMOUNT2, SysUmount2Handle);
+
+/// # do_umount2 - 执行卸载文件系统的函数
+///
+/// 这个函数用于卸载指定的文件系统。
+///
+/// ## 参数
+///
+/// - dirfd: i32 - 目录文件描述符,用于指定要卸载的文件系统的根目录。
+/// - target: &str - 要卸载的文件系统的目标路径。
+/// - _flag: UmountFlag - 卸载标志,目前未使用。
+///
+/// ## 返回值
+///
+/// - Ok(Arc<MountFS>): 成功时返回文件系统的 Arc 引用。
+/// - Err(SystemError): 出错时返回系统错误。
+///
+/// ## 错误处理
+///
+/// 如果指定的路径没有对应的文件系统,或者在尝试卸载时发生错误,将返回错误。
+pub fn do_umount2(
+    dirfd: i32,
+    target: &str,
+    _flag: UmountFlag,
+) -> Result<Arc<MountFS>, SystemError> {
+    let (work, rest) = user_path_at(&ProcessManager::current_pcb(), dirfd, target)?;
+    let path = work.absolute_path()? + &rest;
+
+    if let Some(fs) = MOUNT_LIST().remove(path) {
+        // Todo: 占用检测
+        fs.umount()?;
+        return Ok(fs);
+    }
+    return Err(SystemError::EINVAL);
+}
+
+bitflags! {
+    pub struct UmountFlag: i32 {
+        const DEFAULT = 0;          /* Default call to umount. */
+        const MNT_FORCE = 1;        /* Force unmounting.  */
+        const MNT_DETACH = 2;       /* Just detach from the tree.  */
+        const MNT_EXPIRE = 4;       /* Mark for expiry.  */
+        const UMOUNT_NOFOLLOW = 8;  /* Don't follow symlink on umount.  */
+    }
+}

+ 8 - 154
kernel/src/filesystem/vfs/vcore.rs

@@ -13,24 +13,18 @@ use crate::{
         procfs::procfs_init,
         ramfs::RamFS,
         sysfs::sysfs_init,
-        vfs::{
-            mount::MountFS, syscall::ModeType, AtomicInodeId, FileSystem, FileType, MAX_PATHLEN,
-        },
+        vfs::{mount::MountFS, syscall::ModeType, AtomicInodeId, FileSystem, FileType},
     },
-    libs::spinlock::SpinLock,
     mm::truncate::truncate_inode_pages,
     process::ProcessManager,
-    syscall::user_access::check_and_clone_cstr,
 };
 
 use super::{
-    fcntl::AtFlags,
     file::FileMode,
-    mount::{init_mountlist, MOUNT_LIST},
+    mount::init_mountlist,
     stat::LookUpFlags,
-    syscall::UmountFlag,
     utils::{rsplit_path, user_path_at},
-    FilePrivateData, IndexNode, InodeId, VFS_MAX_FOLLOW_SYMLINK_TIMES,
+    IndexNode, InodeId, VFS_MAX_FOLLOW_SYMLINK_TIMES,
 };
 
 /// 当没有指定根文件系统时,尝试的根文件系统列表
@@ -124,9 +118,9 @@ fn migrate_virtual_filesystem(new_fs: Arc<dyn FileSystem>) -> Result<(), SystemE
     return Ok(());
 }
 
-fn try_find_gendisk_as_rootfs(path: &str) -> Option<Arc<GenDisk>> {
+pub(crate) fn try_find_gendisk(path: &str) -> Option<Arc<GenDisk>> {
     if let Some(gd) = block_dev_manager().lookup_gendisk_by_path(path) {
-        info!("Use {} as rootfs", path);
+        // info!("Use {} as rootfs", path);
         return Some(gd);
     }
     return None;
@@ -136,12 +130,12 @@ pub fn mount_root_fs() -> Result<(), SystemError> {
     info!("Try to mount root fs...");
     block_dev_manager().print_gendisks();
     let gendisk = if let Some(rootfs_dev_path) = ROOTFS_PATH_PARAM.value_str() {
-        try_find_gendisk_as_rootfs(rootfs_dev_path)
+        try_find_gendisk(rootfs_dev_path)
             .unwrap_or_else(|| panic!("Failed to find rootfs device {}", rootfs_dev_path))
     } else {
         ROOTFS_TRY_LIST
             .iter()
-            .find_map(|&path| try_find_gendisk_as_rootfs(path))
+            .find_map(|&path| try_find_gendisk(path))
             .ok_or(SystemError::ENODEV)?
     };
 
@@ -157,6 +151,7 @@ pub fn mount_root_fs() -> Result<(), SystemError> {
     }
     let fatfs: Arc<FATFileSystem> = fatfs.unwrap();
     let r = migrate_virtual_filesystem(fatfs);
+
     if r.is_err() {
         error!("Failed to migrate virtual filesyst  em to FAT32!");
         loop {
@@ -286,147 +281,6 @@ pub fn do_unlink_at(dirfd: i32, path: &str) -> Result<u64, SystemError> {
     return Ok(0);
 }
 
-pub fn do_symlinkat(from: *const u8, newdfd: i32, to: *const u8) -> Result<usize, SystemError> {
-    let oldname = check_and_clone_cstr(from, Some(MAX_PATHLEN))?
-        .into_string()
-        .map_err(|_| SystemError::EINVAL)?;
-    let newname = check_and_clone_cstr(to, Some(MAX_PATHLEN))?
-        .into_string()
-        .map_err(|_| SystemError::EINVAL)?;
-    let from = oldname.as_str().trim();
-    let to = newname.as_str().trim();
-
-    // TODO: 添加权限检查,确保进程拥有目标路径的权限
-
-    let pcb = ProcessManager::current_pcb();
-    let (old_begin_inode, old_remain_path) = user_path_at(&pcb, AtFlags::AT_FDCWD.bits(), from)?;
-    // info!("old_begin_inode={:?}", old_begin_inode.metadata());
-    let _ =
-        old_begin_inode.lookup_follow_symlink(&old_remain_path, VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
-
-    // 得到新创建节点的父节点
-    let (new_begin_inode, new_remain_path) = user_path_at(&pcb, newdfd, to)?;
-    let (new_name, new_parent_path) = rsplit_path(&new_remain_path);
-    let new_parent = new_begin_inode
-        .lookup_follow_symlink(new_parent_path.unwrap_or("/"), VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
-    // info!("new_parent={:?}", new_parent.metadata());
-
-    if new_parent.metadata()?.file_type != FileType::Dir {
-        return Err(SystemError::ENOTDIR);
-    }
-
-    let new_inode = new_parent.create_with_data(
-        new_name,
-        FileType::SymLink,
-        ModeType::from_bits_truncate(0o777),
-        0,
-    )?;
-
-    let buf = old_remain_path.as_bytes();
-    let len = buf.len();
-    new_inode.write_at(0, len, buf, SpinLock::new(FilePrivateData::Unused).lock())?;
-    return Ok(0);
-}
-
-/// # do_mount - 挂载文件系统
-///
-/// 将给定的文件系统挂载到指定的挂载点。
-///
-/// 此函数会检查是否已经挂载了相同的文件系统,如果已经挂载,则返回错误。
-/// 它还会处理符号链接,并确保挂载点是有效的。
-///
-/// ## 参数
-///
-/// - `fs`: Arc<dyn FileSystem>,要挂载的文件系统。
-/// - `mount_point`: &str,挂载点路径。
-///
-/// ## 返回值
-///
-/// - `Ok(Arc<MountFS>)`: 挂载成功后返回挂载的文件系统。
-/// - `Err(SystemError)`: 挂载失败时返回错误。
-pub fn do_mount(fs: Arc<dyn FileSystem>, mount_point: &str) -> Result<Arc<MountFS>, SystemError> {
-    let (current_node, rest_path) = user_path_at(
-        &ProcessManager::current_pcb(),
-        AtFlags::AT_FDCWD.bits(),
-        mount_point,
-    )?;
-    let inode = current_node.lookup_follow_symlink(&rest_path, VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
-    if let Some((_, rest, _fs)) = MOUNT_LIST().get_mount_point(mount_point) {
-        if rest.is_empty() {
-            return Err(SystemError::EBUSY);
-        }
-    }
-    // 移至IndexNode.mount()来记录
-    return inode.mount(fs);
-}
-
-/// # do_mount_mkdir - 在指定挂载点创建目录并挂载文件系统
-///
-/// 在指定的挂载点创建一个目录,并将其挂载到文件系统中。如果挂载点已经存在,并且不是空的,
-/// 则会返回错误。成功时,会返回一个新的挂载文件系统的引用。
-///
-/// ## 参数
-///
-/// - `fs`: FileSystem - 文件系统的引用,用于创建和挂载目录。
-/// - `mount_point`: &str - 挂载点路径,用于创建和挂载目录。
-///
-/// ## 返回值
-///
-/// - `Ok(Arc<MountFS>)`: 成功挂载文件系统后,返回挂载文件系统的共享引用。
-/// - `Err(SystemError)`: 挂载失败时,返回系统错误。
-pub fn do_mount_mkdir(
-    fs: Arc<dyn FileSystem>,
-    mount_point: &str,
-) -> Result<Arc<MountFS>, SystemError> {
-    let inode = do_mkdir_at(
-        AtFlags::AT_FDCWD.bits(),
-        mount_point,
-        FileMode::from_bits_truncate(0o755),
-    )?;
-    if let Some((_, rest, _fs)) = MOUNT_LIST().get_mount_point(mount_point) {
-        if rest.is_empty() {
-            return Err(SystemError::EBUSY);
-        }
-    }
-    return inode.mount(fs);
-}
-
-/// # do_umount2 - 执行卸载文件系统的函数
-///
-/// 这个函数用于卸载指定的文件系统。
-///
-/// ## 参数
-///
-/// - dirfd: i32 - 目录文件描述符,用于指定要卸载的文件系统的根目录。
-/// - target: &str - 要卸载的文件系统的目标路径。
-/// - _flag: UmountFlag - 卸载标志,目前未使用。
-///
-/// ## 返回值
-///
-/// - Ok(Arc<MountFS>): 成功时返回文件系统的 Arc 引用。
-/// - Err(SystemError): 出错时返回系统错误。
-///
-/// ## 错误处理
-///
-/// 如果指定的路径没有对应的文件系统,或者在尝试卸载时发生错误,将返回错误。
-pub fn do_umount2(
-    dirfd: i32,
-    target: &str,
-    _flag: UmountFlag,
-) -> Result<Arc<MountFS>, SystemError> {
-    let (work, rest) = user_path_at(&ProcessManager::current_pcb(), dirfd, target)?;
-    let path = work.absolute_path()? + &rest;
-    let do_umount = || -> Result<Arc<MountFS>, SystemError> {
-        if let Some(fs) = MOUNT_LIST().remove(path) {
-            // Todo: 占用检测
-            fs.umount()?;
-            return Ok(fs);
-        }
-        return Err(SystemError::EINVAL);
-    };
-    return do_umount();
-}
-
 pub(super) fn do_file_lookup_at(
     dfd: i32,
     path: &str,

+ 0 - 30
kernel/src/syscall/mod.rs

@@ -270,20 +270,6 @@ impl Syscall {
                 Self::unlinkat(dirfd, path, flags)
             }
 
-            #[cfg(target_arch = "x86_64")]
-            SYS_SYMLINK => {
-                let oldname = args[0] as *const u8;
-                let newname = args[1] as *const u8;
-                Self::symlink(oldname, newname)
-            }
-
-            SYS_SYMLINKAT => {
-                let oldname = args[0] as *const u8;
-                let newdfd = args[1] as i32;
-                let newname = args[2] as *const u8;
-                Self::symlinkat(oldname, newdfd, newname)
-            }
-
             #[cfg(target_arch = "x86_64")]
             SYS_RMDIR => {
                 let path = args[0] as *const u8;
@@ -786,22 +772,6 @@ impl Syscall {
                 Err(SystemError::ENOSYS)
             }
 
-            SYS_MOUNT => {
-                let source = args[0] as *const u8;
-                let target = args[1] as *const u8;
-                let filesystemtype = args[2] as *const u8;
-                let mountflags = args[3];
-                let data = args[4] as *const u8; // 额外的mount参数,实现自己的mountdata来获取
-                return Self::mount(source, target, filesystemtype, mountflags, data);
-            }
-
-            SYS_UMOUNT2 => {
-                let target = args[0] as *const u8;
-                let flags = args[1] as i32;
-                Self::umount2(target, flags)?;
-                return Ok(0);
-            }
-
             #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
             SYS_NEWFSTATAT => Self::newfstatat(args[0] as i32, args[1], args[2], args[3] as u32),
 

+ 64 - 0
tools/make_fs_image.sh

@@ -0,0 +1,64 @@
+#!/bin/bash
+set -euo pipefail
+
+# 检查是否以 root 权限运行
+if [[ $EUID -ne 0 ]]; then
+    echo "错误:此脚本必须以 root 权限运行!"
+    exit 1
+fi
+
+
+# 获取项目根目录(无论从哪里调用脚本)
+root_folder="$(cd "$(dirname "$0")/.." && pwd)"
+echo "项目根目录:$root_folder"
+mkdir -p "$root_folder/bin"
+
+LOOP_DEVICE=""
+
+# 自动清理 trap
+cleanup() {
+    if [[ -n "${LOOP_DEVICE:-}" ]]; then
+        echo "清理:释放 loop 设备 $LOOP_DEVICE"
+        losetup -d "$LOOP_DEVICE" || echo "警告:无法释放 $LOOP_DEVICE"
+    fi
+}
+trap cleanup EXIT
+
+# 创建 ext4 镜像
+EXT4_IMG="ext4.img"
+EXT4_SIZE="1G"
+echo "创建 ext4 镜像 $EXT4_IMG 大小 $EXT4_SIZE"
+dd if=/dev/zero of="$EXT4_IMG" bs=1M count=1024 status=progress
+
+LOOP_DEVICE=$(losetup --find --show "$EXT4_IMG")
+echo "loop 设备为 $LOOP_DEVICE"
+echo "格式化为 ext4..."
+mkfs.ext4 "$LOOP_DEVICE"
+losetup -d "$LOOP_DEVICE"
+LOOP_DEVICE=""
+
+mv "$EXT4_IMG" "$root_folder/bin/$EXT4_IMG"
+echo "ext4 镜像已保存到 $root_folder/bin/$EXT4_IMG"
+
+# 创建 fat 镜像
+FAT_IMG="fat.img"
+FAT_SIZE="64M"
+echo "创建 fat 镜像 $FAT_IMG 大小 $FAT_SIZE"
+dd if=/dev/zero of="$FAT_IMG" bs=1M count=64 status=progress
+
+# 创建分区表和分区
+parted -s "$FAT_IMG" mklabel msdos
+parted -s "$FAT_IMG" mkpart primary fat32 1MiB 100%
+
+LOOP_DEVICE=$(losetup --find --partscan --show "$FAT_IMG")
+PARTITION="${LOOP_DEVICE}p1"
+echo "loop 设备为 $LOOP_DEVICE,分区为 $PARTITION"
+sleep 1  # 等待内核识别分区
+
+echo "格式化为 fat32..."
+mkfs.vfat -F 32 "$PARTITION"
+losetup -d "$LOOP_DEVICE"
+LOOP_DEVICE=""
+
+mv "$FAT_IMG" "$root_folder/bin/$FAT_IMG"
+echo "fat 镜像已保存到 $root_folder/bin/$FAT_IMG"

+ 16 - 0
tools/run-qemu.sh

@@ -77,9 +77,13 @@ RISCV64_UBOOT_PATH="arch/riscv64/u-boot-${UBOOT_VERSION}-riscv64"
 
 
 DISK_NAME="disk-image-${ARCH}.img"
+EXT4_DISK_NAME="ext4.img"
+FAT_DISK_NAME="fat.img"
 
 QEMU=$(which qemu-system-${ARCH})
 QEMU_DISK_IMAGE="../bin/${DISK_NAME}"
+QEMU_EXT4_DISK_IMAGE="../bin/${EXT4_DISK_NAME}"
+QEMU_FAT_DISK_IMAGE="../bin/${FAT_DISK_NAME}"
 QEMU_MEMORY="512M"
 QEMU_MEMORY_BACKEND="dragonos-qemu-shm.ram"
 QEMU_MEMORY_BACKEND_PATH_PREFIX="/dev/shm"
@@ -96,6 +100,12 @@ QEMU_ACCELARATE=""
 QEMU_ARGUMENT=" -no-reboot "
 QEMU_DEVICES=""
 
+if [ -f "${QEMU_EXT4_DISK_IMAGE}" ]; then
+  QEMU_DRIVE+=" -drive id=ext4disk,file=${QEMU_EXT4_DISK_IMAGE},if=none,format=raw"
+fi
+if [ -f "${QEMU_FAT_DISK_IMAGE}" ]; then
+  QEMU_DRIVE+=" -drive id=fatdisk,file=${QEMU_FAT_DISK_IMAGE},if=none,format=raw"
+fi
 
 check_dependencies
 
@@ -125,6 +135,12 @@ if [ ${ARCH} == "i386" ] || [ ${ARCH} == "x86_64" ]; then
     else
       QEMU_DEVICES_DISK="-device virtio-blk-pci,drive=disk -device pci-bridge,chassis_nr=1,id=pci.1 -device pcie-root-port "
     fi
+    if [ -f "${QEMU_EXT4_DISK_IMAGE}" ]; then
+      QEMU_DEVICES_DISK+=" -device virtio-blk-pci,drive=ext4disk"
+    fi
+    if [ -f "${QEMU_FAT_DISK_IMAGE}" ]; then
+      QEMU_DEVICES_DISK+=" -device virtio-blk-pci,drive=fatdisk"
+    fi
 
 elif [ ${ARCH} == "riscv64" ]; then
     QEMU_MACHINE=" -machine virt,memory-backend=${QEMU_MEMORY_BACKEND} -cpu sifive-u54 "

+ 3 - 0
user/apps/test-mount-ext4/.gitignore

@@ -0,0 +1,3 @@
+/target
+Cargo.lock
+/install/

+ 12 - 0
user/apps/test-mount-ext4/Cargo.toml

@@ -0,0 +1,12 @@
+[package]
+name = "test-mount-ext4"
+version = "0.1.0"
+edition = "2021"
+description = "测试是否可以通过mount系统调用来挂载ext4类型的硬盘"
+authors = ["sparkzky <sparkhhhhhhhhhh@outlook.com>"]
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+errno = "0.3.9"
+libc = "0.2"

+ 56 - 0
user/apps/test-mount-ext4/Makefile

@@ -0,0 +1,56 @@
+TOOLCHAIN=
+RUSTFLAGS=
+
+ifdef DADK_CURRENT_BUILD_DIR
+# 如果是在dadk中编译,那么安装到dadk的安装目录中
+	INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR)
+else
+# 如果是在本地编译,那么安装到当前目录下的install目录中
+	INSTALL_DIR = ./install
+endif
+
+ifeq ($(ARCH), x86_64)
+	export RUST_TARGET=x86_64-unknown-linux-musl
+else ifeq ($(ARCH), riscv64)
+	export RUST_TARGET=riscv64gc-unknown-linux-gnu
+else 
+# 默认为x86_86,用于本地编译
+	export RUST_TARGET=x86_64-unknown-linux-musl
+endif
+
+run:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET)
+
+build:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET)
+
+clean:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET)
+
+test:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET)
+
+doc:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc --target $(RUST_TARGET)
+
+fmt:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt
+
+fmt-check:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check
+
+run-release:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release
+
+build-release:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release
+
+clean-release:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release
+
+test-release:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release
+
+.PHONY: install
+install:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force

+ 50 - 0
user/apps/test-mount-ext4/src/main.rs

@@ -0,0 +1,50 @@
+use core::ffi::{c_char, c_void};
+use errno::errno;
+use libc::{mount, umount, MS_BIND};
+use std::fs;
+use std::path::Path;
+use std::time;
+
+fn main() {
+    let ext4_path = Path::new("mnt/ext4");
+    let dir = fs::create_dir_all(ext4_path);
+    if dir.is_err() {
+        panic!("mkdir /mnt/ext4 fail.");
+    }
+
+    let clock = time::Instant::now();
+    let source = b"/dev/vdb\0".as_ptr() as *const c_char;
+    let target = b"/mnt/ext4\0".as_ptr() as *const c_char;
+    let fstype = b"ext4\0".as_ptr() as *const c_char;
+    let flags = MS_BIND;
+    let data = std::ptr::null() as *const c_void;
+    let result = unsafe { mount(source, target, fstype, flags, data) };
+
+    let path = Path::new("mnt/ext4/tmp");
+    let dir = fs::create_dir_all(path);
+    if dir.is_err() {
+        panic!("mkdir /mnt/ext4/tmp fail.");
+    }
+    let _ = fs::remove_dir_all(path);
+
+    if result == 0 {
+        println!("Mount successful");
+    } else {
+        let err = errno();
+        println!("Mount failed with error code: {}", err.0);
+    }
+    let dur = clock.elapsed();
+    println!("mount costing time: {} ns", dur.as_nanos());
+
+    let result = unsafe { umount(target) };
+    if result != 0 {
+        let err = errno();
+        println!("Mount failed with error code: {}", err.0);
+    }
+    assert_eq!(result, 0, "Umount ext4 failed");
+    println!("Umount successful");
+
+    let _ = fs::remove_dir_all(ext4_path);
+
+    println!("All tests passed!");
+}

+ 3 - 0
user/apps/test-mount-fat/.gitignore

@@ -0,0 +1,3 @@
+/target
+Cargo.lock
+/install/

+ 12 - 0
user/apps/test-mount-fat/Cargo.toml

@@ -0,0 +1,12 @@
+[package]
+name = "test-mount-fat"
+version = "0.1.0"
+edition = "2021"
+description = "测试fat的挂载"
+authors = ["sparkzky <sparkhhhhhhhhhh@outlook.com>"]
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+errno = "0.3.9"
+libc = "0.2"

+ 56 - 0
user/apps/test-mount-fat/Makefile

@@ -0,0 +1,56 @@
+TOOLCHAIN=
+RUSTFLAGS=
+
+ifdef DADK_CURRENT_BUILD_DIR
+# 如果是在dadk中编译,那么安装到dadk的安装目录中
+	INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR)
+else
+# 如果是在本地编译,那么安装到当前目录下的install目录中
+	INSTALL_DIR = ./install
+endif
+
+ifeq ($(ARCH), x86_64)
+	export RUST_TARGET=x86_64-unknown-linux-musl
+else ifeq ($(ARCH), riscv64)
+	export RUST_TARGET=riscv64gc-unknown-linux-gnu
+else 
+# 默认为x86_86,用于本地编译
+	export RUST_TARGET=x86_64-unknown-linux-musl
+endif
+
+run:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET)
+
+build:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET)
+
+clean:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET)
+
+test:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET)
+
+doc:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc --target $(RUST_TARGET)
+
+fmt:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt
+
+fmt-check:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check
+
+run-release:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release
+
+build-release:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release
+
+clean-release:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release
+
+test-release:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release
+
+.PHONY: install
+install:
+	RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force

+ 51 - 0
user/apps/test-mount-fat/src/main.rs

@@ -0,0 +1,51 @@
+use core::ffi::{c_char, c_void};
+use errno::errno;
+use libc::{mount, umount, MS_BIND};
+use std::fs;
+use std::path::Path;
+use std::time;
+
+fn main() {
+    let fat_path = Path::new("mnt/fat");
+    let dir = fs::create_dir_all(fat_path);
+    if dir.is_err() {
+        panic!("mkdir /mnt/fat fail.");
+    }
+
+    let clock = time::Instant::now();
+    // 这里根据实际情况更改硬盘的地址
+    let source = b"/dev/vdc1\0".as_ptr() as *const c_char;
+    let target = b"/mnt/fat\0".as_ptr() as *const c_char;
+    let fstype = b"vfat\0".as_ptr() as *const c_char;
+    let flags = MS_BIND;
+    let data = std::ptr::null() as *const c_void;
+    let result = unsafe { mount(source, target, fstype, flags, data) };
+
+    let path = Path::new("mnt/fat/tmp");
+    let dir = fs::create_dir_all(path);
+    if dir.is_err() {
+        panic!("mkdir /mnt/fat/tmp fail.");
+    }
+    let _ = fs::remove_dir_all(path);
+
+    if result == 0 {
+        println!("Mount successful");
+    } else {
+        let err = errno();
+        println!("Mount failed with error code: {}", err.0);
+    }
+    let dur = clock.elapsed();
+    println!("mount costing time: {} ns", dur.as_nanos());
+
+    let result = unsafe { umount(target) };
+    if result != 0 {
+        let err = errno();
+        println!("Mount failed with error code: {}", err.0);
+    }
+    assert_eq!(result, 0, "Umount fat failed");
+    println!("Umount successful");
+
+    let _ = fs::remove_dir_all(fat_path);
+
+    println!("All tests passed!");
+}

+ 51 - 0
user/dadk/config/test_mount_ext4_0_1_0.toml

@@ -0,0 +1,51 @@
+# 用户程序名称
+name = "test_mount_ext4"
+# 版本号
+version = "1.0.0"
+# 用户程序描述信息
+description = "测试是否可以通过mount系统调用来挂载ext4类型的硬盘"
+# (可选)默认: false 是否只构建一次,如果为true,DADK会在构建成功后,将构建结果缓存起来,下次构建时,直接使用缓存的构建结果
+build-once = false
+#  (可选) 默认: false 是否只安装一次,如果为true,DADK会在安装成功后,不再重复安装
+install-once = false
+# 目标架构
+# 可选值:"x86_64", "aarch64", "riscv64"
+target-arch = ["x86_64"]
+# 任务源
+[task-source]
+# 构建类型
+# 可选值:"build-from_source", "install-from-prebuilt"
+type = "build-from-source"
+# 构建来源
+# "build_from_source" 可选值:"git", "local", "archive"
+# "install_from_prebuilt" 可选值:"local", "archive"
+source = "local"
+# 路径或URL
+source-path = "user/apps/test-mount-ext4"
+# 构建相关信息
+[build]
+# (可选)构建命令
+build-command = "make install"
+# 安装相关信息
+[install]
+# (可选)安装到DragonOS的路径
+in-dragonos-path = "/"
+# 清除相关信息
+[clean]
+# (可选)清除命令
+clean-command = "make clean"
+# (可选)依赖项
+# 注意:如果没有依赖项,忽略此项,不允许只留一个[[depends]]
+# [[depends]]
+# name = "depend1"
+# version = "0.1.1"
+# [[depends]]
+# name = "depend2"
+# version = "0.1.2"
+# (可选)环境变量
+# [[envs]]
+# key = "PATH"
+# value = "/usr/bin"
+# [[envs]]
+# key = "LD_LIBRARY_PATH"
+# value = "/usr/lib"

+ 51 - 0
user/dadk/config/test_mount_fat_0_1_0.toml

@@ -0,0 +1,51 @@
+# 用户程序名称
+name = "test_mount_fat"
+# 版本号
+version = "1.0.0"
+# 用户程序描述信息
+description = "测试是否可以通过mount系统调用来挂载fat类型的硬盘"
+# (可选)默认: false 是否只构建一次,如果为true,DADK会在构建成功后,将构建结果缓存起来,下次构建时,直接使用缓存的构建结果
+build-once = false
+#  (可选) 默认: false 是否只安装一次,如果为true,DADK会在安装成功后,不再重复安装
+install-once = false
+# 目标架构
+# 可选值:"x86_64", "aarch64", "riscv64"
+target-arch = ["x86_64"]
+# 任务源
+[task-source]
+# 构建类型
+# 可选值:"build-from_source", "install-from-prebuilt"
+type = "build-from-source"
+# 构建来源
+# "build_from_source" 可选值:"git", "local", "archive"
+# "install_from_prebuilt" 可选值:"local", "archive"
+source = "local"
+# 路径或URL
+source-path = "user/apps/test-mount-fat"
+# 构建相关信息
+[build]
+# (可选)构建命令
+build-command = "make install"
+# 安装相关信息
+[install]
+# (可选)安装到DragonOS的路径
+in-dragonos-path = "/"
+# 清除相关信息
+[clean]
+# (可选)清除命令
+clean-command = "make clean"
+# (可选)依赖项
+# 注意:如果没有依赖项,忽略此项,不允许只留一个[[depends]]
+# [[depends]]
+# name = "depend1"
+# version = "0.1.1"
+# [[depends]]
+# name = "depend2"
+# version = "0.1.2"
+# (可选)环境变量
+# [[envs]]
+# key = "PATH"
+# value = "/usr/bin"
+# [[envs]]
+# key = "LD_LIBRARY_PATH"
+# value = "/usr/lib"