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:
parent
823e1933dd
commit
1e574d89fa
|
@ -12,6 +12,7 @@ VFS是DragonOS文件系统的核心,它提供了一套统一的文件系统接
|
|||
- 提供文件系统的抽象(FileSystem)
|
||||
- 提供IndexNode抽象
|
||||
- 提供文件系统的缓存、同步机制(尚未实现)
|
||||
- 支持将硬盘设备挂载到文件系统上(目前支持EXT4和vfat类型的virtio硬盘)
|
||||
|
||||
|
||||
.. toctree::
|
||||
|
@ -20,4 +21,5 @@ VFS是DragonOS文件系统的核心,它提供了一套统一的文件系统接
|
|||
|
||||
design
|
||||
api
|
||||
mountable_fs
|
||||
|
||||
|
|
|
@ -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`测试程序来验证
|
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -8,3 +8,5 @@ pub extern crate ringbuffer;
|
|||
|
||||
pub extern crate crc;
|
||||
pub extern crate xarray;
|
||||
|
||||
pub extern crate another_ext4;
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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)]
|
||||
|
|
|
@ -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)]
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
// 完全不考虑性能的实现
|
||||
pub mod filesystem;
|
||||
pub mod gendisk;
|
||||
pub mod inode;
|
|
@ -1,4 +1,5 @@
|
|||
pub mod bpb;
|
||||
pub mod entry;
|
||||
pub mod fs;
|
||||
mod mount;
|
||||
pub mod utils;
|
||||
|
|
|
@ -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");
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
|
@ -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);
|
|
@ -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. */
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
||||
|
|
|
@ -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"
|
|
@ -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 "
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
/target
|
||||
Cargo.lock
|
||||
/install/
|
|
@ -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"
|
|
@ -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
|
|
@ -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!");
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
/target
|
||||
Cargo.lock
|
||||
/install/
|
|
@ -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"
|
|
@ -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
|
|
@ -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!");
|
||||
}
|
|
@ -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"
|
|
@ -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"
|
Loading…
Reference in New Issue