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>
This commit is contained in:
火花 2025-06-29 17:05:06 +08:00 committed by GitHub
parent 823e1933dd
commit 1e574d89fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 2211 additions and 367 deletions

View File

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

View File

@ -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` 中镜像传入的顺序自动分配abc等等其中的数字表示分区号。
所以目前需要挂载硬盘的话,可以更改`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
kernel/Cargo.lock generated
View File

@ -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",
]

View File

@ -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" }

View File

@ -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]

View File

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

View File

@ -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" }

View File

@ -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()
}
}

View File

@ -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)]

View File

@ -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)]

View File

@ -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()
}
}

View File

@ -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,

View File

@ -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 {

View File

@ -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> {

View File

@ -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,

View File

@ -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);
}

View File

@ -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> {

View File

@ -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,

View File

@ -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")
}
}

View File

@ -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();
}
}

View File

@ -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")
}
}

View File

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

View File

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

View File

@ -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");

View File

@ -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;

View File

@ -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 {

View File

@ -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> {

View File

@ -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);

View File

@ -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);
}

View File

@ -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,

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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. */
}
}

View File

@ -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,

View File

@ -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
tools/make_fs_image.sh Executable file
View File

@ -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"

View File

@ -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
user/apps/test-mount-ext4/.gitignore vendored Normal file
View File

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

View File

@ -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"

View File

@ -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

View File

@ -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
user/apps/test-mount-fat/.gitignore vendored Normal file
View File

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

View File

@ -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"

View File

@ -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

View File

@ -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!");
}

View File

@ -0,0 +1,51 @@
# 用户程序名称
name = "test_mount_ext4"
# 版本号
version = "1.0.0"
# 用户程序描述信息
description = "测试是否可以通过mount系统调用来挂载ext4类型的硬盘"
# (可选)默认: false 是否只构建一次如果为trueDADK会在构建成功后将构建结果缓存起来下次构建时直接使用缓存的构建结果
build-once = false
# (可选) 默认: false 是否只安装一次如果为trueDADK会在安装成功后不再重复安装
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"

View File

@ -0,0 +1,51 @@
# 用户程序名称
name = "test_mount_fat"
# 版本号
version = "1.0.0"
# 用户程序描述信息
description = "测试是否可以通过mount系统调用来挂载fat类型的硬盘"
# (可选)默认: false 是否只构建一次如果为trueDADK会在构建成功后将构建结果缓存起来下次构建时直接使用缓存的构建结果
build-once = false
# (可选) 默认: false 是否只安装一次如果为trueDADK会在安装成功后不再重复安装
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"