diff --git a/docs/kernel/device/index.rst b/docs/kernel/device/index.rst index dc9184c7f..e189422d5 100644 --- a/docs/kernel/device/index.rst +++ b/docs/kernel/device/index.rst @@ -6,3 +6,4 @@ Device :maxdepth: 1 tty + loop_device diff --git a/docs/kernel/device/loop_device.md b/docs/kernel/device/loop_device.md new file mode 100644 index 000000000..5ec1469f6 --- /dev/null +++ b/docs/kernel/device/loop_device.md @@ -0,0 +1,127 @@ +# Loop Device 架构设计 + +本文档阐述 DragonOS 中 loop 设备子系统的架构设计思路,用于指导开发和后续演进。 + +## 问题背景 + +在操作系统开发中,我们经常面临这样的需求: +- 如何将一个镜像文件当作块设备使用? +- 如何在不重启系统的情况下动态创建/删除虚拟块设备? + +loop 设备正是解决这些问题的关键组件。 + +## 系统架构定位 + +loop 设备在 DragonOS 架构中扮演着"虚拟化桥梁"的角色: + +``` +用户态应用 + ↓ +loop-control 接口 (字符设备) + ↓ +LoopManager (设备生命周期管理) + ↓ +LoopDevice[] (虚拟块设备数组) + ↓ +块设备层 ←→ 后端文件系统 +``` + +这种分层设计的核心思想是:**将控制平面与数据平面分离**。 + +## 核心设计哲学 + +### 1. 状态驱动的设备管理 + +我们仿照linux设计引入状态机来管理设备生命周期 + +``` +Unbound → Bound → Rundown → Deleting + ↓ ↓ ↓ +Deleting Unbound Deleting +``` + +**设计考量**: +- 防止非法状态转换(如直接在 Bound 状态删除设备) +- 提供清晰的设备生命周期语义 +- 为未来的扩展(如设备热插拔)奠定基础 + +### 2. 双重接口策略 + +我们的设计刻意区分了两种接口: + +**字符控制接口** (`/dev/loop-control`): +- 负责设备的生命周期管理 +- 提供用户友好的设备分配/回收机制 +- 与 Linux 标准接口保持兼容 + +**块设备接口** (`/dev/loopX`): +- 专注于数据读写功能 +- 提供标准的块设备语义 +- 支持偏移、大小限制等高级功能 + +**设计价值**:这种分离使得控制逻辑与数据路径互不干扰,提高了系统的可维护性。 + +### 3. 安全性优先 + +在与用户态交互时,我们采用了多重安全检查: + +- **参数边界检查**:所有偏移和大小都必须 LBA 对齐 +- **内存安全**:使用 `UserBufferReader/Writer` 进行用户态数据拷贝 +- **权限验证**:只读设备拒绝写入操作 +- **状态验证**:每个操作前都检查当前设备状态是否允许 + +## 模块协作架构 + +### LoopManager 的定位 +LoopManager 不是简单的设备数组管理器,而是整个子系统的"调度中心": + +- **设备分配策略**:采用"就近分配"原则,优先复用空闲设备 +- **资源池管理**:预注册 8 个设备,避免运行时分配开销 +- **并发安全**:所有设备操作都在锁保护下进行 + +### LoopDevice 的抽象设计 +LoopDevice 的核心抽象是"**后端文件的块设备视图**": + +``` +用户视角 内部实现 +/dev/loop0 ←→ 文件偏移 + 大小限制 + 块0-100 文件偏移0-51200 + 块101-200 文件偏移51200-102400 +``` + +这种设计允许用户将文件的任意部分映射为块设备,为容器等应用场景提供了极大的灵活性。 + +## 关键设计 + +### 为什么选择 256 个设备上限? +- 足够满足大多数应用场景需求 +- 避免无限制增长导致的资源耗尽 +- 与 Linux 系统默认上限保持一致,保证兼容性 + +### 为什么预注册 8 个设备? +- 覆盖常见的测试场景(通常不超过 4-5 个) +- 减少首次使用的等待时间 +- 提供一个合理的初始工作集 + +### 为什么使用 SpinLock 而不是其他锁? +- loop 设备操作大多是短时操作 +- 避免复杂的锁层级和死锁问题 +- 简化实现,提高性能 + +## 兼容性考量 + +我们的设计在很大程度上参考了 Linux loop 驱动的接口,这是有意的选择: + +1. **用户态软件兼容**:现有的 loop 工具无需修改即可使用 +2. **API 契约一致性**:避免因接口差异导致的潜在问题 +3. **社区知识复用**:开发者可以复用现有的 loop 设备知识 + +## 总结 + +DragonOS 的 loop 设备设计遵循以下核心原则: +1. **架构清晰**:控制平面与数据平面分离 +2. **状态安全**:基于状态机的设备生命周期管理 +3. **接口兼容**:与 Linux 标准接口保持兼容 +4. **扩展友好**:为未来功能预留架构空间 +5. **测试完备**:通过多层级测试保证质量 + diff --git a/kernel/crates/ida/src/lib.rs b/kernel/crates/ida/src/lib.rs index 29c89e159..28149f864 100644 --- a/kernel/crates/ida/src/lib.rs +++ b/kernel/crates/ida/src/lib.rs @@ -105,6 +105,35 @@ impl IdAllocator { return None; } + /// 按指定的id分配(预占)一个id + /// + /// 主要用于“用户请求指定编号”的场景 + /// + /// ## 返回 + /// + /// - `Some(id)`: 分配成功 + /// - `None`: id 越界、已被占用或无可用 id + pub fn alloc_specific(&mut self, id: usize) -> Option { + if unlikely(self.available() == 0) { + return None; + } + + if id < self.min_id || id >= self.max_id { + return None; + } + + if self.exists(id) { + return None; + } + + self.xarray.store(id as u64, EmptyIdaItem); + self.used += 1; + + // 为了让后续的 alloc() 更均匀地分配,尽量从下一个位置开始搜索 + self.current_id = (id + 1).min(self.max_id.saturating_sub(1)); + Some(id) + } + /// 检查id是否存在 /// /// ## 参数 diff --git a/kernel/src/driver/base/block/gendisk/mod.rs b/kernel/src/driver/base/block/gendisk/mod.rs index 45466b55a..b250e08c3 100644 --- a/kernel/src/driver/base/block/gendisk/mod.rs +++ b/kernel/src/driver/base/block/gendisk/mod.rs @@ -1,4 +1,5 @@ use core::{ + convert::TryFrom, ops::{Deref, DerefMut}, sync::atomic::{AtomicU32, Ordering}, }; @@ -10,8 +11,10 @@ use alloc::{ use hashbrown::HashMap; use system_error::SystemError; +use super::block_device::{BlockDevice, BlockId, GeneralBlockRange, LBA_SIZE}; use crate::{ driver::base::device::device_number::DeviceNumber, + driver::block::loop_device::LoopDevice, filesystem::{ devfs::{DevFS, DeviceINode, LockedDevFSInode}, vfs::{utils::DName, IndexNode, InodeMode, Metadata}, @@ -19,8 +22,6 @@ use crate::{ libs::{rwlock::RwLock, spinlock::SpinLockGuard}, }; -use super::block_device::{BlockDevice, BlockId, GeneralBlockRange, LBA_SIZE}; - const MINORS_PER_DISK: u32 = 256; #[derive(Debug)] @@ -221,22 +222,34 @@ impl IndexNode for GenDisk { fn read_at( &self, - _offset: usize, - _len: usize, - _buf: &mut [u8], + offset: usize, + len: usize, + buf: &mut [u8], _data: SpinLockGuard, ) -> Result { - Err(SystemError::EPERM) + if len == 0 { + return Ok(0); + } + if len > buf.len() { + return Err(SystemError::ENOBUFS); + } + self.read_at_bytes(&mut buf[..len], offset) } fn write_at( &self, - _offset: usize, - _len: usize, - _buf: &[u8], + offset: usize, + len: usize, + buf: &[u8], _data: SpinLockGuard, ) -> Result { - Err(SystemError::EPERM) + if len == 0 { + return Ok(0); + } + if len > buf.len() { + return Err(SystemError::E2BIG); + } + self.write_at_bytes(&buf[..len], offset) } fn list(&self) -> Result, system_error::SystemError> { @@ -244,7 +257,19 @@ impl IndexNode for GenDisk { } fn metadata(&self) -> Result { - Ok(self.metadata.clone()) + let mut meta = self.metadata.clone(); + let bdev = self.block_device(); + let range = bdev.disk_range(); + let blocks = range.lba_end.saturating_sub(range.lba_start); + let size_in_bytes = blocks.saturating_mul(LBA_SIZE); + + meta.size = match i64::try_from(size_in_bytes) { + Ok(val) => val, + Err(_) => i64::MAX, + }; + meta.blocks = blocks; + meta.blk_size = LBA_SIZE; + Ok(meta) } fn dname(&self) -> Result { @@ -273,6 +298,20 @@ impl IndexNode for GenDisk { ) -> Result<(), SystemError> { Ok(()) } + + fn ioctl( + &self, + cmd: u32, + data: usize, + private_data: &crate::filesystem::vfs::FilePrivateData, + ) -> Result { + let bdev = self.block_device(); + if let Some(loop_dev) = BlockDevice::as_any_ref(&*bdev).downcast_ref::() { + loop_dev.ioctl(cmd, data, private_data) + } else { + Err(SystemError::ENOSYS) + } + } } impl DeviceINode for GenDisk { diff --git a/kernel/src/driver/base/block/manager.rs b/kernel/src/driver/base/block/manager.rs index 3eb735ce8..31893d146 100644 --- a/kernel/src/driver/base/block/manager.rs +++ b/kernel/src/driver/base/block/manager.rs @@ -1,6 +1,6 @@ use core::{fmt::Formatter, sync::atomic::AtomicU32}; -use alloc::sync::Arc; +use alloc::{sync::Arc, vec::Vec}; use hashbrown::HashMap; use system_error::SystemError; use unified_init::macros::unified_init; @@ -11,7 +11,7 @@ use crate::{ device::{device_number::Major, DevName}, }, filesystem::{ - devfs::devfs_register, + devfs::{devfs_register, devfs_unregister}, mbr::MbrDiskPartionTable, vfs::{utils::DName, IndexNode}, }, @@ -166,14 +166,59 @@ impl BlockDevManager { } /// 卸载磁盘设备 - #[allow(dead_code)] - pub fn unregister(&self, dev: &Arc) { + pub fn unregister(&self, dev: &Arc) -> Result<(), SystemError> { + // 事务式卸载: + // 1) 先尝试从 devfs 卸载所有 gendisk 节点(可能失败) + // 2) 全部成功后,再更新 BlockDevManager 与 blk_meta 元数据 + // + // 这样即便中途失败,也不会出现“manager 已移除/元数据已清空,但 devfs 仍残留节点”的不可恢复不一致。 + { + let inner = self.inner(); + if !inner.disks.contains_key(dev.dev_name()) { + return Err(SystemError::ENOENT); + } + } + + let blk_meta = dev.blkdev_meta(); + let gendisks: Vec> = { + let meta_inner = blk_meta.inner(); + meta_inner.gendisks.values().cloned().collect() + }; + + let mut unregistered: Vec> = Vec::new(); + for gendisk in &gendisks { + let dname = gendisk.dname()?; + if let Err(e) = devfs_unregister(dname.as_ref(), gendisk.clone()) { + // 回滚:尽量把已卸载的重新注册回 devfs,保持系统可用/一致。 + for rg in unregistered.into_iter() { + if let Ok(rname) = rg.dname() { + let _ = devfs_register(rname.as_ref(), rg.clone()); + } + } + return Err(e); + } + unregistered.push(gendisk.clone()); + } + + // 全部 devfs 卸载成功:再移除 manager 记录并清空 gendisks 元数据 let mut inner = self.inner(); - inner.disks.remove(dev.dev_name()); - // todo: 这里应该callback一下磁盘设备,但是现在还没实现热插拔,所以暂时没做这里 - todo!("BlockDevManager: unregister disk") - } + if inner.disks.remove(dev.dev_name()).is_none() { + // 理论上不应发生:在 devfs 卸载过程中该设备被并发移除 + // 尽力回滚 devfs,以免留下“manager 不存在但 devfs 存在/不存在”的混乱状态 + drop(inner); + for rg in unregistered.into_iter() { + if let Ok(rname) = rg.dname() { + let _ = devfs_register(rname.as_ref(), rg.clone()); + } + } + return Err(SystemError::ENOENT); + } + drop(inner); + let mut meta_inner = blk_meta.inner(); + meta_inner.gendisks.clear(); + Ok(()) + } /// 通过路径查找gendisk /// /// # 参数 diff --git a/kernel/src/driver/base/block/mod.rs b/kernel/src/driver/base/block/mod.rs index a34479836..1d3211973 100644 --- a/kernel/src/driver/base/block/mod.rs +++ b/kernel/src/driver/base/block/mod.rs @@ -2,7 +2,6 @@ pub mod block_device; pub mod disk_info; pub mod gendisk; pub mod manager; - #[derive(Debug)] #[allow(dead_code)] pub enum SeekFrom { diff --git a/kernel/src/driver/base/device/device_number.rs b/kernel/src/driver/base/device/device_number.rs index 4af80b699..79f19d1cb 100644 --- a/kernel/src/driver/base/device/device_number.rs +++ b/kernel/src/driver/base/device/device_number.rs @@ -42,6 +42,8 @@ impl Major { pub const fn data(&self) -> u32 { self.0 } + pub const LOOP_MAJOR: Self = Self::new(7); + pub const LOOP_CONTROL_MAJOR: Self = Self::new(10); } impl Hash for Major { diff --git a/kernel/src/driver/block/loop_device/constants.rs b/kernel/src/driver/block/loop_device/constants.rs new file mode 100644 index 000000000..e627f4d54 --- /dev/null +++ b/kernel/src/driver/block/loop_device/constants.rs @@ -0,0 +1,93 @@ +use bitflags::bitflags; +/// Loop 设备基础名称 +pub const LOOP_BASENAME: &str = "loop"; + +/// Loop-control 设备基础名称 +pub const LOOP_CONTROL_BASENAME: &str = "loop-control"; + +/// Loop-control 设备的次设备号 +pub const LOOP_CONTROL_MINOR: u32 = 237; + +/// I/O 排空超时时间 (毫秒) +pub const LOOP_IO_DRAIN_TIMEOUT_MS: u32 = 30_000; + +/// I/O 排空检查间隔 (微秒) +pub const LOOP_IO_DRAIN_CHECK_INTERVAL_US: u32 = 10_000; + +/// Loop 设备 ioctl 命令 +#[repr(u32)] +#[derive(Debug, FromPrimitive)] +pub enum LoopIoctl { + /// 设置后端文件描述符 + LoopSetFd = 0x4C00, + /// 清除后端文件绑定 + LoopClrFd = 0x4C01, + /// 设置设备状态 (32位兼容) + LoopSetStatus = 0x4C02, + /// 获取设备状态 (32位兼容) + LoopGetStatus = 0x4C03, + /// 设置设备状态 (64位) + LoopSetStatus64 = 0x4C04, + /// 获取设备状态 (64位) + LoopGetStatus64 = 0x4C05, + /// 更换后端文件描述符 + LoopChangeFd = 0x4C06, + /// 重新计算设备容量 + LoopSetCapacity = 0x4C07, + /// 设置直接I/O模式 + LoopSetDirectIo = 0x4C08, + /// 设置块大小 + LoopSetBlockSize = 0x4C09, + /// 配置设备 + LoopConfigure = 0x4C0A, +} + +/// Loop-control 设备 ioctl 命令 +#[repr(u32)] +#[derive(Debug, FromPrimitive)] +pub enum LoopControlIoctl { + /// 添加新的 loop 设备 + Add = 0x4C80, + /// 删除 loop 设备 + Remove = 0x4C81, + /// 获取空闲的 loop 设备 + GetFree = 0x4C82, +} + +bitflags! { + /// Loop 设备标志位 + #[derive(Default)] + pub struct LoopFlags: u32 { + /// 只读模式 + const READ_ONLY = 1 << 0; + } +} + +/// Loop 设备状态信息结构体 (64位版本) +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct LoopStatus64 { + /// 文件内偏移量 + pub lo_offset: u64, + /// 大小限制 (0 表示无限制) + pub lo_sizelimit: u64, + /// 标志位 + pub lo_flags: u32, + /// 填充字段 + pub __pad: u32, +} + +/// Loop 设备状态 +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum LoopState { + /// 未绑定状态 + Unbound, + /// 已绑定状态 + Bound, + /// 正在停止运行 (不再接受新 I/O) + Rundown, + /// 正在排空活跃 I/O + Draining, + /// 正在删除 + Deleting, +} diff --git a/kernel/src/driver/block/loop_device/driver.rs b/kernel/src/driver/block/loop_device/driver.rs new file mode 100644 index 000000000..842f22af4 --- /dev/null +++ b/kernel/src/driver/block/loop_device/driver.rs @@ -0,0 +1,151 @@ +use crate::{ + driver::base::{ + device::{ + bus::Bus, + driver::{Driver, DriverCommonData}, + Device, IdTable, + }, + kobject::{KObjType, KObject, KObjectCommonData, KObjectState, LockedKObjectState}, + kset::KSet, + }, + filesystem::kernfs::KernFSInode, + libs::{ + rwlock::{RwLockReadGuard, RwLockWriteGuard}, + spinlock::{SpinLock, SpinLockGuard}, + }, +}; +use alloc::{ + string::{String, ToString}, + sync::{Arc, Weak}, + vec::Vec, +}; +use core::{ + any::Any, + fmt::{Debug, Formatter}, +}; + +use super::constants::LOOP_BASENAME; + +/// Loop设备驱动 +/// 参考Virtio_blk驱动的实现 +#[derive(Debug)] +#[cast_to([sync] Driver)] +pub struct LoopDeviceDriver { + inner: SpinLock, + kobj_state: LockedKObjectState, +} + +struct InnerLoopDeviceDriver { + driver_common: DriverCommonData, + kobj_common: KObjectCommonData, +} + +impl Debug for InnerLoopDeviceDriver { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.debug_struct("InnerLoopDeviceDriver") + .field("driver_common", &self.driver_common) + .field("kobj_common", &self.kobj_common) + .finish() + } +} + +impl LoopDeviceDriver { + pub fn new() -> Arc { + let inner = InnerLoopDeviceDriver { + driver_common: DriverCommonData::default(), + kobj_common: KObjectCommonData::default(), + }; + Arc::new(Self { + inner: SpinLock::new(inner), + kobj_state: LockedKObjectState::default(), + }) + } + + fn inner(&'_ self) -> SpinLockGuard<'_, InnerLoopDeviceDriver> { + self.inner.lock() + } +} + +impl Driver for LoopDeviceDriver { + fn id_table(&self) -> Option { + Some(IdTable::new(LOOP_BASENAME.to_string(), None)) + } + + fn devices(&self) -> Vec> { + self.inner().driver_common.devices.clone() + } + + fn add_device(&self, device: Arc) { + self.inner().driver_common.push_device(device); + } + + fn delete_device(&self, device: &Arc) { + self.inner().driver_common.delete_device(device); + } + + fn bus(&self) -> Option> { + self.inner().driver_common.bus.clone() + } + + fn set_bus(&self, bus: Option>) { + self.inner().driver_common.bus = bus; + } +} + +impl KObject for LoopDeviceDriver { + fn as_any_ref(&self) -> &dyn Any { + self + } + + fn set_inode(&self, inode: Option>) { + self.inner().kobj_common.kern_inode = inode; + } + + fn inode(&self) -> Option> { + self.inner().kobj_common.kern_inode.clone() + } + + fn parent(&self) -> Option> { + self.inner().kobj_common.parent.clone() + } + + fn set_parent(&self, parent: Option>) { + self.inner().kobj_common.parent = parent; + } + + fn kset(&self) -> Option> { + self.inner().kobj_common.kset.clone() + } + + fn set_kset(&self, kset: Option>) { + self.inner().kobj_common.kset = kset; + } + + fn kobj_type(&self) -> Option<&'static dyn KObjType> { + self.inner().kobj_common.kobj_type + } + + fn set_kobj_type(&self, ktype: Option<&'static dyn KObjType>) { + self.inner().kobj_common.kobj_type = ktype; + } + + fn name(&self) -> String { + LOOP_BASENAME.to_string() + } + + fn set_name(&self, _name: String) { + // do nothing + } + + fn kobj_state(&'_ self) -> RwLockReadGuard<'_, KObjectState> { + self.kobj_state.read() + } + + fn kobj_state_mut(&'_ self) -> RwLockWriteGuard<'_, KObjectState> { + self.kobj_state.write() + } + + fn set_kobj_state(&self, state: KObjectState) { + *self.kobj_state.write() = state; + } +} diff --git a/kernel/src/driver/block/loop_device/loop_control.rs b/kernel/src/driver/block/loop_device/loop_control.rs new file mode 100644 index 000000000..d6aebbe42 --- /dev/null +++ b/kernel/src/driver/block/loop_device/loop_control.rs @@ -0,0 +1,356 @@ +use crate::{ + driver::base::{ + class::Class, + device::{ + bus::Bus, + device_number::{DeviceNumber, Major}, + driver::Driver, + Device, DeviceCommonData, DeviceType, IdTable, + }, + kobject::{KObjType, KObject, KObjectCommonData, KObjectState, LockedKObjectState}, + kset::KSet, + }, + filesystem::{ + devfs::{DevFS, DeviceINode, LockedDevFSInode}, + kernfs::KernFSInode, + vfs::{ + file::FileFlags, FilePrivateData, FileType, IndexNode, InodeFlags, InodeId, InodeMode, + Metadata, + }, + }, + libs::{ + rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, + spinlock::{SpinLock, SpinLockGuard}, + }, + process::ProcessManager, + time::PosixTimeSpec, +}; +use alloc::{ + string::{String, ToString}, + sync::{Arc, Weak}, +}; +use core::{ + any::Any, + fmt::{Debug, Formatter}, +}; +use num_traits::FromPrimitive; +use system_error::SystemError; + +use super::{ + constants::{LoopControlIoctl, LOOP_CONTROL_BASENAME, LOOP_CONTROL_MINOR}, + manager::LoopManager, +}; + +/// Loop-control 设备 +/// +/// 一个字符设备,作为一个抽象接口控制loop设备的创建,绑定和删除 +/// - 设备分配和查找 +/// - 设备绑定和解绑 +/// - 设备状态查询和配置(配置设备参数,如偏移量、大小限制等) +pub struct LoopControlDevice { + inner: SpinLock, + locked_kobj_state: LockedKObjectState, + loop_mgr: Arc, +} + +struct LoopControlDeviceInner { + /// 设备的公共数据 + device_common: DeviceCommonData, + /// KObject的公共数据 + kobject_common: KObjectCommonData, + + parent: RwLock>, + device_inode_fs: RwLock>>, +} + +impl LoopControlDevice { + pub fn new(loop_mgr: Arc) -> Arc { + Arc::new(Self { + inner: SpinLock::new(LoopControlDeviceInner { + kobject_common: KObjectCommonData::default(), + device_common: DeviceCommonData::default(), + parent: RwLock::new(Weak::default()), + device_inode_fs: RwLock::new(None), + }), + locked_kobj_state: LockedKObjectState::default(), + loop_mgr, + }) + } + + fn inner(&'_ self) -> SpinLockGuard<'_, LoopControlDeviceInner> { + self.inner.lock() + } +} + +impl DeviceINode for LoopControlDevice { + fn set_fs(&self, fs: alloc::sync::Weak) { + *self.inner().device_inode_fs.write() = Some(fs); + } + + fn set_parent(&self, parent: Weak) { + *self.inner().parent.write() = parent; + } +} + +impl Debug for LoopControlDevice { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.debug_struct("LoopControlDevice").finish() + } +} + +impl IndexNode for LoopControlDevice { + fn open( + &self, + _data: SpinLockGuard, + _mode: &FileFlags, + ) -> Result<(), SystemError> { + Ok(()) + } + + fn close(&self, _data: SpinLockGuard) -> Result<(), SystemError> { + Ok(()) + } + + /// # 功能 + /// + /// 获取 loop-control 设备的元数据信息。 + /// + /// ## 参数 + /// + /// - 无 + /// + /// ## 返回值 + /// - `Ok(Metadata)`: 成功获取设备元数据 + /// - 包含设备类型、权限、设备号等信息 + fn metadata(&self) -> Result { + let metadata = Metadata { + dev_id: 0, + inode_id: InodeId::new(0), + size: 0, + blk_size: 0, + blocks: 0, + atime: PosixTimeSpec::default(), + mtime: PosixTimeSpec::default(), + ctime: PosixTimeSpec::default(), + btime: PosixTimeSpec::default(), + file_type: FileType::CharDevice, + mode: InodeMode::from_bits_truncate(0o600), + flags: InodeFlags::empty(), + nlinks: 1, + uid: 0, + gid: 0, + raw_dev: DeviceNumber::new(Major::LOOP_CONTROL_MAJOR, LOOP_CONTROL_MINOR), + }; + Ok(metadata) + } + + fn fs(&self) -> Arc { + // loop-control 设备节点由 DevFS 注册;返回其所在的文件系统。 + if let Some(fs) = self + .inner() + .device_inode_fs + .read() + .as_ref() + .and_then(|w| w.upgrade()) + { + return fs; + } + ProcessManager::current_mntns() + .root_inode() + .find("dev") + .expect("LoopControlDevice: DevFS not mounted at /dev") + .fs() + } + + fn ioctl( + &self, + cmd: u32, + data: usize, + _private_data: &FilePrivateData, + ) -> Result { + match LoopControlIoctl::from_u32(cmd) { + Some(LoopControlIoctl::Add) => { + log::info!("Starting LOOP_CTL_ADD ioctl"); + let requested_index = data as u32; + let loop_dev = if requested_index == u32::MAX { + self.loop_mgr.loop_add(None)? + } else { + self.loop_mgr.loop_add(Some(requested_index))? + }; + let minor = { + let inner = loop_dev.inner(); + let minor = inner.device_number.minor(); + log::info!( + "LOOP_CTL_ADD ioctl succeeded, allocated loop device loop{}", + minor + ); + minor + }; + Ok(minor as usize) + } + Some(LoopControlIoctl::Remove) => { + let minor_to_remove = data as u32; + self.loop_mgr.loop_remove(minor_to_remove)?; + Ok(0) + } + Some(LoopControlIoctl::GetFree) => match self.loop_mgr.find_free_minor() { + Some(minor) => Ok(minor as usize), + None => Err(SystemError::ENOSPC), + }, + _ => Err(SystemError::ENOSYS), + } + } + + fn as_any_ref(&self) -> &dyn core::any::Any { + self + } + + fn read_at( + &self, + _offset: usize, + _len: usize, + _buf: &mut [u8], + _data: SpinLockGuard, + ) -> Result { + Err(SystemError::ENOSYS) + } + + fn write_at( + &self, + _offset: usize, + _len: usize, + _buf: &[u8], + _data: SpinLockGuard, + ) -> Result { + Err(SystemError::ENOSYS) + } + + fn list(&self) -> Result, system_error::SystemError> { + Err(SystemError::ENOSYS) + } +} + +impl Device for LoopControlDevice { + fn dev_type(&self) -> DeviceType { + DeviceType::Char + } + + fn id_table(&self) -> IdTable { + IdTable::new(LOOP_CONTROL_BASENAME.to_string(), None) + } + + fn bus(&self) -> Option> { + self.inner().device_common.bus.clone() + } + + fn set_bus(&self, bus: Option>) { + self.inner().device_common.bus = bus; + } + + fn class(&self) -> Option> { + let mut guard = self.inner(); + let r = guard.device_common.class.clone()?.upgrade(); + if r.is_none() { + guard.device_common.class = None; + } + return r; + } + + fn set_class(&self, class: Option>) { + self.inner().device_common.class = class; + } + + fn driver(&self) -> Option> { + let r = self.inner().device_common.driver.clone()?.upgrade(); + if r.is_none() { + self.inner().device_common.driver = None; + } + return r; + } + + fn set_driver(&self, driver: Option>) { + self.inner().device_common.driver = driver; + } + + fn is_dead(&self) -> bool { + false + } + + fn can_match(&self) -> bool { + self.inner().device_common.can_match + } + + fn set_can_match(&self, can_match: bool) { + self.inner().device_common.can_match = can_match; + } + + fn state_synced(&self) -> bool { + true + } + + fn dev_parent(&self) -> Option> { + self.inner().device_common.get_parent_weak_or_clear() + } + + fn set_dev_parent(&self, parent: Option>) { + self.inner().device_common.parent = parent; + } +} + +impl KObject for LoopControlDevice { + fn as_any_ref(&self) -> &dyn Any { + self + } + + fn set_inode(&self, inode: Option>) { + self.inner().kobject_common.kern_inode = inode; + } + + fn inode(&self) -> Option> { + self.inner().kobject_common.kern_inode.clone() + } + + fn parent(&self) -> Option> { + self.inner().kobject_common.parent.clone() + } + + fn set_parent(&self, parent: Option>) { + self.inner().kobject_common.parent = parent; + } + + fn kset(&self) -> Option> { + self.inner().kobject_common.kset.clone() + } + + fn set_kset(&self, kset: Option>) { + self.inner().kobject_common.kset = kset; + } + + fn kobj_type(&self) -> Option<&'static dyn KObjType> { + self.inner().kobject_common.kobj_type + } + + fn set_kobj_type(&self, ktype: Option<&'static dyn KObjType>) { + self.inner().kobject_common.kobj_type = ktype; + } + + fn name(&self) -> String { + LOOP_CONTROL_BASENAME.to_string() + } + + fn set_name(&self, _name: String) { + // do nothing + } + + fn kobj_state(&'_ self) -> RwLockReadGuard<'_, KObjectState> { + self.locked_kobj_state.read() + } + + fn kobj_state_mut(&'_ self) -> RwLockWriteGuard<'_, KObjectState> { + self.locked_kobj_state.write() + } + + fn set_kobj_state(&self, state: KObjectState) { + *self.locked_kobj_state.write() = state; + } +} diff --git a/kernel/src/driver/block/loop_device/loop_device.rs b/kernel/src/driver/block/loop_device/loop_device.rs new file mode 100644 index 000000000..27506714c --- /dev/null +++ b/kernel/src/driver/block/loop_device/loop_device.rs @@ -0,0 +1,1147 @@ +use crate::{ + driver::base::{ + block::{ + block_device::{BlockDevice, BlockId, GeneralBlockRange, LBA_SIZE}, + disk_info::Partition, + manager::BlockDevMeta, + }, + class::Class, + device::{ + bus::Bus, + device_number::{DeviceNumber, Major}, + driver::Driver, + DevName, Device, DeviceCommonData, DeviceType, IdTable, + }, + kobject::{ + KObjType, KObject, KObjectCommonData, KObjectManager, KObjectState, KObjectSysFSOps, + LockedKObjectState, + }, + kset::KSet, + }, + filesystem::{ + devfs::{DevFS, DeviceINode, LockedDevFSInode}, + kernfs::KernFSInode, + sysfs::{AttributeGroup, SysFSOps}, + vfs::{FilePrivateData, FileType, IndexNode, InodeFlags, InodeId, InodeMode, Metadata}, + }, + libs::{ + rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, + spinlock::{SpinLock, SpinLockGuard}, + }, + process::ProcessManager, + syscall::user_access::{UserBufferReader, UserBufferWriter}, + time::{sleep::nanosleep, PosixTimeSpec}, +}; +use alloc::{ + string::{String, ToString}, + sync::{Arc, Weak}, + vec::Vec, +}; +use core::{ + any::Any, + fmt::{Debug, Formatter}, + sync::atomic::{AtomicU32, Ordering}, +}; +use log::{error, info, warn}; +use num_traits::FromPrimitive; +use system_error::SystemError; + +use super::constants::{ + LoopFlags, LoopIoctl, LoopState, LoopStatus64, LOOP_BASENAME, LOOP_IO_DRAIN_CHECK_INTERVAL_US, + LOOP_IO_DRAIN_TIMEOUT_MS, +}; + +/// Loop 设备 KObject 类型 +#[derive(Debug)] +pub struct LoopDeviceKObjType; + +impl KObjType for LoopDeviceKObjType { + fn release(&self, kobj: Arc) { + if let Some(loop_dev) = kobj.as_any_ref().downcast_ref::() { + loop_dev.final_cleanup(); + } + } + + fn sysfs_ops(&self) -> Option<&dyn SysFSOps> { + Some(&KObjectSysFSOps) + } + + fn attribute_groups(&self) -> Option<&'static [&'static dyn AttributeGroup]> { + None + } +} + +pub(super) static LOOP_DEVICE_KOBJ_TYPE: LoopDeviceKObjType = LoopDeviceKObjType; + +/// I/O 操作 RAII 守卫 +struct IoGuard<'a> { + device: &'a LoopDevice, +} + +impl<'a> IoGuard<'a> { + fn new(device: &'a LoopDevice) -> Result { + device.io_start()?; + Ok(Self { device }) + } +} + +impl<'a> Drop for IoGuard<'a> { + fn drop(&mut self) { + self.device.io_end(); + } +} + +/// Loop 设备 +pub struct LoopDevice { + id: usize, + minor: u32, + inner: SpinLock, + block_dev_meta: BlockDevMeta, + locked_kobj_state: LockedKObjectState, + self_ref: Weak, + fs: RwLock>, + parent: RwLock>, + /// 活跃的 I/O 操作计数 + active_io_count: AtomicU32, +} + +/// Loop 设备的私有数据(目前未使用) +#[derive(Debug, Clone, Default)] +pub struct LoopPrivateData; + +/// Loop 设备内部状态 +pub struct LoopDeviceInner { + pub device_number: DeviceNumber, + state: LoopState, + pub file_inode: Option>, + pub file_size: usize, + pub offset: usize, + pub size_limit: usize, + pub flags: LoopFlags, + pub kobject_common: KObjectCommonData, + pub device_common: DeviceCommonData, +} + +impl LoopDeviceInner { + /// 检查状态转换是否有效并执行转换 + /// + /// 注意:调用者必须持有 LoopDeviceInner 的锁 + pub(super) fn set_state(&mut self, new_state: LoopState) -> Result<(), SystemError> { + const VALID_TRANSITIONS: &[(LoopState, LoopState)] = &[ + (LoopState::Unbound, LoopState::Bound), + (LoopState::Bound, LoopState::Unbound), + (LoopState::Bound, LoopState::Rundown), + (LoopState::Rundown, LoopState::Draining), + (LoopState::Rundown, LoopState::Deleting), + (LoopState::Rundown, LoopState::Unbound), + // 允许 Draining 回滚到 Rundown:当 I/O 排空超时/失败时保持拒绝新 I/O, + // 并允许后续重试 drain 或继续删除流程。 + (LoopState::Draining, LoopState::Rundown), + (LoopState::Draining, LoopState::Deleting), + (LoopState::Unbound, LoopState::Deleting), + ]; + if !VALID_TRANSITIONS.contains(&(self.state, new_state)) { + return Err(SystemError::EINVAL); + } + self.state = new_state; + Ok(()) + } + + /// 检查设备是否只读 + #[inline] + pub(super) fn is_read_only(&self) -> bool { + self.flags.contains(LoopFlags::READ_ONLY) + } + + /// 获取当前状态 + #[inline] + pub(super) fn state(&self) -> LoopState { + self.state + } +} + +impl Debug for LoopDevice { + fn fmt(&'_ self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.debug_struct("LoopDevice") + .field("id", &self.id) + .field("devname", &self.block_dev_meta.devname) + .finish() + } +} + +impl LoopDevice { + pub(super) fn inner(&'_ self) -> SpinLockGuard<'_, LoopDeviceInner> { + self.inner.lock() + } + + fn set_file_locked( + inner: &mut LoopDeviceInner, + file_inode: Arc, + file_size: usize, + ) { + inner.file_inode = Some(file_inode); + inner.file_size = file_size; + inner.offset = 0; + inner.size_limit = 0; + } + + fn change_file_locked( + inner: &mut LoopDeviceInner, + file_inode: Arc, + total_size: usize, + read_only: bool, + ) -> Result<(), SystemError> { + if inner.offset > total_size { + return Err(SystemError::EINVAL); + } + let mut effective = total_size - inner.offset; + if inner.size_limit > 0 { + effective = effective.min(inner.size_limit); + } + inner.file_inode = Some(file_inode); + inner.flags = if read_only { + LoopFlags::READ_ONLY + } else { + LoopFlags::empty() + }; + inner.file_size = effective; + Ok(()) + } + + pub fn id(&self) -> usize { + self.id + } + + pub fn minor(&self) -> u32 { + self.minor + } + + /// # 功能 + /// + /// 创建一个未绑定文件的 loop 设备实例。 + /// + /// ## 参数 + /// + /// - `devname`: 设备名称。 + /// - `minor`: 次设备号。 + /// + /// ## 返回值 + /// - `Some(Arc)`: 成功创建的 loop 设备。 + /// - `None`: 内存不足或创建失败。 + pub fn new_empty_loop_device(devname: DevName, id: usize, minor: u32) -> Option> { + let dev = Arc::new_cyclic(|self_ref| Self { + id, + minor, + inner: SpinLock::new(LoopDeviceInner { + file_inode: None, + file_size: 0, + device_number: DeviceNumber::new(Major::LOOP_MAJOR, minor), + offset: 0, + size_limit: 0, + flags: LoopFlags::empty(), + kobject_common: KObjectCommonData::default(), + device_common: DeviceCommonData::default(), + state: LoopState::Unbound, + }), + block_dev_meta: BlockDevMeta::new(devname, Major::LOOP_MAJOR), + locked_kobj_state: LockedKObjectState::default(), + self_ref: self_ref.clone(), + fs: RwLock::new(Weak::default()), + parent: RwLock::new(Weak::default()), + active_io_count: AtomicU32::new(0), + }); + + // 设置 KObjType + dev.set_kobj_type(Some(&LOOP_DEVICE_KOBJ_TYPE)); + + Some(dev) + } + + fn compute_effective_size( + inode: &Arc, + offset: usize, + size_limit: usize, + ) -> Result { + let metadata = inode.metadata()?; + if metadata.size < 0 { + return Err(SystemError::EINVAL); + } + let total_size = metadata.size as usize; + if offset > total_size { + return Err(SystemError::EINVAL); + } + let mut effective = total_size - offset; + if size_limit > 0 { + effective = effective.min(size_limit); + } + Ok(effective) + } + + fn recalc_effective_size(&self) -> Result<(), SystemError> { + // 通过“快照 -> 计算 -> CAS式提交”的方式避免: + // - 持锁期间调用 metadata() 导致阻塞 + // - offset/limit/inode 并发变化时写入错误的 file_size + for _ in 0..3 { + let (file_inode, offset, size_limit) = { + let inner = self.inner(); + (inner.file_inode.clone(), inner.offset, inner.size_limit) + }; + + let inode = file_inode.ok_or(SystemError::ENODEV)?; + let effective = Self::compute_effective_size(&inode, offset, size_limit)?; + + let mut inner = self.inner(); + let still_same_inode = inner + .file_inode + .as_ref() + .map(|cur| Arc::ptr_eq(cur, &inode)) + .unwrap_or(false); + if still_same_inode && inner.offset == offset && inner.size_limit == size_limit { + inner.file_size = effective; + return Ok(()); + } + } + Err(SystemError::EAGAIN_OR_EWOULDBLOCK) + } + + pub fn is_bound(&self) -> bool { + matches!(self.inner().state(), LoopState::Bound) + } + + /// # 功能 + /// + /// 将文件绑定到 loop 设备并设置访问权限。 + /// + /// ## 参数 + /// + /// - `file_inode`: 需要绑定的文件节点。 + /// - `read_only`: 是否以只读方式绑定。 + /// + /// ## 返回值 + /// - `Ok(())`: 成功绑定。 + /// - `Err(SystemError)`: 绑定失败的原因。 + pub fn bind_file( + &self, + file_inode: Arc, + read_only: bool, + ) -> Result<(), SystemError> { + // 先在锁外拿到 metadata,避免在持锁期间做可能阻塞的操作 + let metadata = file_inode.metadata()?; + if metadata.size < 0 { + return Err(SystemError::EINVAL); + } + + let total_size = metadata.size as usize; + + // 在同一个临界区里完成状态检查 + 状态转换 + 写入 file_inode, + // 避免 set_file() 先修改数据、再 set_state() 失败造成“半更新”。 + let mut inner = self.inner(); + match inner.state() { + LoopState::Unbound => {} + LoopState::Bound => return Err(SystemError::EBUSY), + LoopState::Rundown | LoopState::Draining | LoopState::Deleting => { + return Err(SystemError::ENODEV); + } + } + + inner.set_state(LoopState::Bound)?; + Self::set_file_locked(&mut inner, file_inode, total_size); + inner.flags = if read_only { + LoopFlags::READ_ONLY + } else { + LoopFlags::empty() + }; + drop(inner); + self.recalc_effective_size()?; + Ok(()) + } + + /// # 功能 + /// + /// 清除 loop 设备的文件绑定并复位状态。 + /// + /// ## 参数 + /// + /// - 无。 + /// + /// ## 返回值 + /// - `Ok(())`: 成功清除。 + /// - `Err(SystemError)`: 清除过程中的错误。 + pub fn clear_file(&self) -> Result<(), SystemError> { + let mut inner = self.inner(); + match inner.state() { + LoopState::Bound | LoopState::Rundown => inner.set_state(LoopState::Unbound)?, + LoopState::Unbound => {} + LoopState::Draining => return Err(SystemError::EBUSY), + LoopState::Deleting => { + // 在删除流程中,允许清理文件 + // 状态已经是Deleting,无需改变 + } + } + + inner.file_inode = None; + inner.file_size = 0; + inner.offset = 0; + inner.size_limit = 0; + inner.flags = LoopFlags::empty(); + Ok(()) + } + + fn validate_loop_status64_params(info: &LoopStatus64) -> Result<(), SystemError> { + if !info.lo_offset.is_multiple_of(LBA_SIZE as u64) { + return Err(SystemError::EINVAL); + } + if info.lo_offset > usize::MAX as u64 || info.lo_sizelimit > usize::MAX as u64 { + return Err(SystemError::EINVAL); + } + if info.lo_sizelimit != 0 && !info.lo_sizelimit.is_multiple_of(LBA_SIZE as u64) { + return Err(SystemError::EINVAL); + } + if LoopFlags::from_bits(info.lo_flags).is_none() { + return Err(SystemError::EINVAL); + } + Ok(()) + } + + /// 设置 loop 设备的状态(64 位版本)。 + /// + /// ## 参数 + /// + /// - `user_ptr`: 用户空间传入的 `LoopStatus64` 结构体指针。 + /// + /// ## 返回值 + /// - `Ok(())`: 状态设置成功。 + /// - `Err(SystemError::EINVAL)`: 无效的参数或标志位。 + /// - `Err(SystemError::ENXIO)`: 设备未绑定或已卸载。 + fn set_status64(&self, user_ptr: usize) -> Result<(), SystemError> { + if user_ptr == 0 { + return Err(SystemError::EINVAL); + } + + let reader = UserBufferReader::new::( + user_ptr as *const LoopStatus64, + core::mem::size_of::(), + true, + )?; + let info: LoopStatus64 = reader.buffer_protected(0)?.read_one(0)?; + Self::validate_loop_status64_params(&info)?; + + let new_offset = info.lo_offset as usize; + let new_limit = if info.lo_sizelimit == 0 { + 0 + } else { + info.lo_sizelimit as usize + }; + + // 先拿到 inode 快照,在锁外计算新的 effective size,最后在锁内一次性提交: + // 避免出现 “offset 已更新但 file_size 仍旧值” 的竞态窗口。 + let inode_snapshot = { + let inner = self.inner(); + if !matches!(inner.state(), LoopState::Bound | LoopState::Rundown) { + return Err(SystemError::ENXIO); + } + inner.file_inode.clone().ok_or(SystemError::ENODEV)? + }; + + let new_effective = Self::compute_effective_size(&inode_snapshot, new_offset, new_limit)?; + + let mut inner = self.inner(); + if !matches!(inner.state(), LoopState::Bound | LoopState::Rundown) { + return Err(SystemError::ENXIO); + } + let still_same_inode = inner + .file_inode + .as_ref() + .map(|cur| Arc::ptr_eq(cur, &inode_snapshot)) + .unwrap_or(false); + if !still_same_inode { + // backing file 并发变化(或被清除),拒绝本次更新,避免写入不一致的 size。 + return Err(SystemError::EBUSY); + } + inner.offset = new_offset; + inner.size_limit = new_limit; + inner.flags = LoopFlags::from_bits_truncate(info.lo_flags); + inner.file_size = new_effective; + Ok(()) + } + + /// # 功能 + /// + /// 获取 loop 设备的 LoopStatus64 信息并写回用户态。 + /// + /// ## 参数 + /// + /// - `user_ptr`: 用户态缓冲区地址。 + /// + /// ## 返回值 + /// - `Ok(())`: 信息写回成功。 + /// - `Err(SystemError)`: 读取状态失败。 + fn get_status64(&self, user_ptr: usize) -> Result<(), SystemError> { + if user_ptr == 0 { + return Err(SystemError::EINVAL); + } + + let info = { + let inner = self.inner(); + if !matches!(inner.state(), LoopState::Bound | LoopState::Rundown) { + return Err(SystemError::ENXIO); + } + LoopStatus64 { + lo_offset: inner.offset as u64, + lo_sizelimit: inner.size_limit as u64, + lo_flags: inner.flags.bits(), + __pad: 0, + } + }; + + let mut writer = UserBufferWriter::new::( + user_ptr as *mut LoopStatus64, + core::mem::size_of::(), + true, + )?; + writer.buffer_protected(0)?.write_one(0, &info)?; + Ok(()) + } + + fn set_status(&self, user_ptr: usize) -> Result<(), SystemError> { + self.set_status64(user_ptr) + } + + fn get_status(&self, user_ptr: usize) -> Result<(), SystemError> { + self.get_status64(user_ptr) + } + + /// # 功能 + /// + /// 将 loop 设备切换到新的文件描述符。 + /// + /// ## 参数 + /// + /// - `new_file_fd`: 新的文件描述符。 + /// + /// ## 返回值 + /// - `Ok(())`: 成功切换。 + /// - `Err(SystemError)`: 切换失败原因。 + fn change_fd(&self, new_file_fd: i32) -> Result<(), SystemError> { + let fd_table = ProcessManager::current_pcb().fd_table(); + let file = { + let guard = fd_table.read(); + guard.get_file_by_fd(new_file_fd) + } + .ok_or(SystemError::EBADF)?; + + let read_only = file.flags().is_read_only(); + + let inode = file.inode(); + let metadata = inode.metadata()?; + match metadata.file_type { + FileType::File | FileType::BlockDevice => {} + _ => return Err(SystemError::EINVAL), + } + + if metadata.size < 0 { + return Err(SystemError::EINVAL); + } + let total_size = metadata.size as usize; + + // 单次持锁完成校验+提交,避免“先读快照再提交”期间状态变化导致不一致 + let mut inner = self.inner(); + match inner.state() { + LoopState::Bound => {} + _ => return Err(SystemError::ENODEV), + } + if inner.file_inode.is_none() { + return Err(SystemError::ENODEV); + } + Self::change_file_locked(&mut inner, inode, total_size, read_only)?; + Ok(()) + } + + fn set_capacity(&self, _arg: usize) -> Result<(), SystemError> { + self.recalc_effective_size()?; + Ok(()) + } + + /// # 功能 + /// + /// I/O 操作开始时调用,增加活跃 I/O 计数 + /// + /// ## 返回值 + /// - `Ok(())`: 成功增加计数 + /// - `Err(SystemError::ENODEV)`: 设备正在删除,拒绝新的 I/O + fn io_start(&self) -> Result<(), SystemError> { + let inner = self.inner(); + if matches!( + inner.state(), + LoopState::Rundown | LoopState::Draining | LoopState::Deleting + ) { + return Err(SystemError::ENODEV); + } + self.active_io_count.fetch_add(1, Ordering::AcqRel); + // 通过显式 drop 延长锁守卫的生命周期,避免 NLL 提前释放导致 TOCTOU 竞态 + drop(inner); + Ok(()) + } + + /// # 功能 + /// + /// I/O 操作完成时调用,减少活跃 I/O 计数 + fn io_end(&self) { + let prev = self.active_io_count.fetch_sub(1, Ordering::AcqRel); + debug_assert!(prev > 0, "Loop device I/O count underflow"); + } + + /// # 功能 + /// + /// 进入 Rundown 状态,停止接受新的 I/O 请求 + /// + /// ## 返回值 + /// - `Ok(())`: 成功进入 Rundown 状态 + /// - `Err(SystemError)`: 状态转换失败 + pub(super) fn enter_rundown_state(&self) -> Result<(), SystemError> { + let mut inner = self.inner(); + match inner.state() { + LoopState::Bound => { + inner.set_state(LoopState::Rundown)?; + info!( + "Loop device loop{} entering rundown state", + inner.device_number.minor() + ); + } + LoopState::Unbound => { + // 空设备可以直接删除 + inner.set_state(LoopState::Deleting)?; + info!( + "Loop device loop{} is unbound, skipping to deleting state", + inner.device_number.minor() + ); + } + LoopState::Rundown => {} + LoopState::Draining | LoopState::Deleting => { + return Err(SystemError::EBUSY); + } + } + Ok(()) + } + + /// # 功能 + /// + /// 等待所有活跃的 I/O 操作完成 + /// + /// ## 返回值 + /// - `Ok(())`: 所有 I/O 已完成 + /// - `Err(SystemError::ETIMEDOUT)`: 等待超时 + pub(super) fn drain_active_io(&self) -> Result<(), SystemError> { + let mut inner = self.inner(); + if matches!(inner.state(), LoopState::Rundown) { + inner.set_state(LoopState::Draining)?; + info!( + "Loop device loop{} entering draining state", + inner.device_number.minor() + ); + } + drop(inner); + let max_checks = LOOP_IO_DRAIN_TIMEOUT_MS * 1000 / LOOP_IO_DRAIN_CHECK_INTERVAL_US; + let sleep_ts = PosixTimeSpec::new( + 0, + (LOOP_IO_DRAIN_CHECK_INTERVAL_US as i64).saturating_mul(1000), + ); + + for _i in 0..max_checks { + let count = self.active_io_count.load(Ordering::Acquire); + if count == 0 { + break; + } + + let _ = nanosleep(sleep_ts); + } + + let final_count = self.active_io_count.load(Ordering::Acquire); + if final_count != 0 { + error!( + "Timeout waiting for I/O to drain on loop{}: {} operations still active", + self.minor(), + final_count + ); + // 超时:从 Draining 回滚到 Rundown,保持拒绝新 I/O,并允许后续重试 drain。 + // 注意:这里不能留在 Draining,否则删除流程会永久卡死(enter_rundown_state 返回 EBUSY)。 + let mut inner = self.inner(); + if matches!(inner.state(), LoopState::Draining) { + let _ = inner.set_state(LoopState::Rundown); + } + return Err(SystemError::ETIMEDOUT); + } + + info!( + "All I/O operations drained for loop device loop{}", + self.minor() + ); + + let mut inner = self.inner(); + inner.set_state(LoopState::Deleting)?; + + Ok(()) + } + + /// # 功能 + /// + /// 从 sysfs 中移除设备 + /// + /// ## 返回值 + /// - `Ok(())`: 成功移除 + /// - `Err(SystemError)`: 移除失败 + pub(super) fn remove_from_sysfs(&self) -> Result<(), SystemError> { + // 使用 KObjectManager 从 sysfs 中移除 + if let Some(kobj) = self.self_ref.upgrade() { + KObjectManager::remove_kobj(kobj as Arc); + info!("Removed loop device loop{} from sysfs", self.minor()); + } + Ok(()) + } + + /// # 功能 + /// + /// 最终清理函数,由 KObjType::release 调用 + /// 执行设备删除的最后清理工作 + fn final_cleanup(&self) { + info!( + "Final cleanup for loop device loop{} (id {})", + self.minor(), + self.id() + ); + let mut inner = self.inner(); + if let Some(file_inode) = inner.file_inode.take() { + drop(file_inode); + warn!( + "File inode was still present during final cleanup for loop{}", + self.minor() + ); + } + inner.file_size = 0; + inner.offset = 0; + inner.size_limit = 0; + info!("Loop device loop{} cleanup complete", self.minor()); + } +} + +impl KObject for LoopDevice { + fn as_any_ref(&self) -> &dyn Any { + self + } + + fn set_inode(&self, inode: Option>) { + self.inner().kobject_common.kern_inode = inode; + } + + fn inode(&self) -> Option> { + self.inner().kobject_common.kern_inode.clone() + } + + fn parent(&self) -> Option> { + self.inner().kobject_common.parent.clone() + } + + fn set_parent(&self, parent: Option>) { + self.inner().kobject_common.parent = parent; + } + + fn kset(&self) -> Option> { + self.inner().kobject_common.kset.clone() + } + + fn set_kset(&self, kset: Option>) { + self.inner().kobject_common.kset = kset; + } + + fn kobj_type(&self) -> Option<&'static dyn KObjType> { + self.inner().kobject_common.kobj_type + } + + fn set_kobj_type(&self, ktype: Option<&'static dyn KObjType>) { + self.inner().kobject_common.kobj_type = ktype; + } + + fn name(&self) -> String { + LOOP_BASENAME.to_string() + } + + fn set_name(&self, _name: String) { + // do nothing,不支持设置loop为别的名称 + } + + fn kobj_state(&'_ self) -> RwLockReadGuard<'_, KObjectState> { + self.locked_kobj_state.read() + } + + fn kobj_state_mut(&'_ self) -> RwLockWriteGuard<'_, KObjectState> { + self.locked_kobj_state.write() + } + + fn set_kobj_state(&self, state: KObjectState) { + *self.locked_kobj_state.write() = state; + } +} + +impl IndexNode for LoopDevice { + fn fs(&self) -> Arc { + // 设备通常通过 DevFS 的包装 inode 访问;这里返回其所在的文件系统。 + // 优先使用 devfs 注册时注入的 Weak,避免在正常路径上做路径查找。 + if let Some(fs) = self.fs.read().upgrade() { + return fs; + } + // 兜底:从当前挂载命名空间中找到 /dev 并取其 fs。 + // 该路径在系统正常初始化后应始终存在。 + ProcessManager::current_mntns() + .root_inode() + .find("dev") + .expect("LoopDevice: DevFS not mounted at /dev") + .fs() + } + + fn as_any_ref(&self) -> &dyn core::any::Any { + self + } + + fn read_at( + &self, + offset: usize, + len: usize, + buf: &mut [u8], + _data: SpinLockGuard, + ) -> Result { + if len > buf.len() { + return Err(SystemError::ENOBUFS); + } + BlockDevice::read_at_bytes(self, offset, len, buf) + } + + fn write_at( + &self, + offset: usize, + len: usize, + buf: &[u8], + _data: SpinLockGuard, + ) -> Result { + if len > buf.len() { + return Err(SystemError::E2BIG); + } + BlockDevice::write_at_bytes(self, offset, len, &buf[..len]) + } + + fn list(&self) -> Result, system_error::SystemError> { + Err(SystemError::ENOSYS) + } + + fn metadata(&self) -> Result { + let (inode, file_size, devnum) = { + let inner = self.inner(); + let inode = inner.file_inode.clone().ok_or(SystemError::EPERM)?; + (inode, inner.file_size, inner.device_number) + }; + + let file_metadata = inode.metadata()?; + let metadata = Metadata { + dev_id: 0, + inode_id: InodeId::new(0), + size: file_size as i64, + blk_size: LBA_SIZE, + blocks: file_size.div_ceil(LBA_SIZE), + atime: file_metadata.atime, + mtime: file_metadata.mtime, + ctime: file_metadata.ctime, + btime: file_metadata.btime, + file_type: FileType::BlockDevice, + mode: InodeMode::from_bits_truncate(0o644), + flags: InodeFlags::empty(), + nlinks: 1, + uid: 0, + gid: 0, + raw_dev: devnum, + }; + Ok(metadata) + } + + fn ioctl( + &self, + cmd: u32, + data: usize, + _private_data: &FilePrivateData, + ) -> Result { + let ioctl_cmd = LoopIoctl::from_u32(cmd).ok_or(SystemError::ENOSYS)?; + + match ioctl_cmd { + LoopIoctl::LoopSetFd => { + let file_fd = data as i32; + let fd_table = ProcessManager::current_pcb().fd_table(); + let file = { + let guard = fd_table.read(); + guard.get_file_by_fd(file_fd) + } + .ok_or(SystemError::EBADF)?; + let read_only = file.flags().is_read_only(); + let inode = file.inode(); + let metadata = inode.metadata()?; + match metadata.file_type { + FileType::File | FileType::BlockDevice => {} + _ => return Err(SystemError::EINVAL), + } + + self.bind_file(inode, read_only)?; + Ok(0) + } + LoopIoctl::LoopClrFd => { + self.clear_file()?; + Ok(0) + } + LoopIoctl::LoopSetStatus => { + self.set_status(data)?; + Ok(0) + } + LoopIoctl::LoopGetStatus => { + self.get_status(data)?; + Ok(0) + } + LoopIoctl::LoopSetStatus64 => { + self.set_status64(data)?; + Ok(0) + } + LoopIoctl::LoopGetStatus64 => { + self.get_status64(data)?; + Ok(0) + } + LoopIoctl::LoopChangeFd => { + self.change_fd(data as i32)?; + Ok(0) + } + LoopIoctl::LoopSetCapacity => { + self.set_capacity(data)?; + Ok(0) + } + _ => Err(SystemError::ENOSYS), + } + } +} + +impl DeviceINode for LoopDevice { + fn set_fs(&self, fs: alloc::sync::Weak) { + *self.fs.write() = fs; + } + + fn set_parent(&self, parent: Weak) { + *self.parent.write() = parent; + } +} + +impl Device for LoopDevice { + fn dev_type(&self) -> DeviceType { + DeviceType::Block + } + + fn id_table(&self) -> IdTable { + IdTable::new(LOOP_BASENAME.to_string(), None) + } + + fn bus(&self) -> Option> { + self.inner().device_common.bus.clone() + } + + fn set_bus(&self, bus: Option>) { + self.inner().device_common.bus = bus; + } + + fn class(&self) -> Option> { + let mut guard = self.inner(); + let r = guard.device_common.class.clone()?.upgrade(); + if r.is_none() { + guard.device_common.class = None; + } + return r; + } + + fn set_class(&self, class: Option>) { + self.inner().device_common.class = class; + } + + fn driver(&self) -> Option> { + let r = self.inner().device_common.driver.clone()?.upgrade(); + if r.is_none() { + self.inner().device_common.driver = None; + } + return r; + } + + fn set_driver(&self, driver: Option>) { + self.inner().device_common.driver = driver; + } + + fn is_dead(&self) -> bool { + false + } + + fn can_match(&self) -> bool { + self.inner().device_common.can_match + } + + fn set_can_match(&self, can_match: bool) { + self.inner().device_common.can_match = can_match; + } + + fn state_synced(&self) -> bool { + true + } + + fn dev_parent(&self) -> Option> { + self.inner().device_common.get_parent_weak_or_clear() + } + + fn set_dev_parent(&self, parent: Option>) { + self.inner().device_common.parent = parent; + } +} + +impl BlockDevice for LoopDevice { + fn dev_name(&self) -> &DevName { + &self.block_dev_meta.devname + } + + fn blkdev_meta(&self) -> &BlockDevMeta { + &self.block_dev_meta + } + + fn disk_range(&self) -> GeneralBlockRange { + let inner = self.inner(); + let blocks = if inner.file_size == 0 { + 0 + } else { + inner.file_size.saturating_add(LBA_SIZE - 1) / LBA_SIZE + }; + drop(inner); + GeneralBlockRange::new(0, blocks).unwrap_or(GeneralBlockRange { + lba_start: 0, + lba_end: 0, + }) + } + + fn read_at_sync( + &self, + lba_id_start: BlockId, + count: usize, + buf: &mut [u8], + ) -> Result { + // 使用 IoGuard 确保 I/O 计数正确管理 + let _io_guard = IoGuard::new(self)?; + + if count == 0 { + return Ok(0); + } + let len = count.checked_mul(LBA_SIZE).ok_or(SystemError::EOVERFLOW)?; + if len > buf.len() { + return Err(SystemError::EINVAL); + } + + let (file_inode, base_offset, limit_end) = { + let inner = self.inner(); + let inode = inner.file_inode.clone().ok_or(SystemError::ENODEV)?; + let limit = inner + .offset + .checked_add(inner.file_size) + .ok_or(SystemError::EOVERFLOW)?; + (inode, inner.offset, limit) + }; + + let block_offset = lba_id_start + .checked_mul(LBA_SIZE) + .ok_or(SystemError::EOVERFLOW)?; + let file_offset = base_offset + .checked_add(block_offset) + .ok_or(SystemError::EOVERFLOW)?; + + let end = file_offset.checked_add(len).ok_or(SystemError::EOVERFLOW)?; + if end > limit_end { + return Err(SystemError::ENOSPC); + } + + let data = SpinLock::new(FilePrivateData::Unused); + let data_guard = data.lock(); + + file_inode.read_at(file_offset, len, &mut buf[..len], data_guard) + } + + fn write_at_sync( + &self, + lba_id_start: BlockId, + count: usize, + buf: &[u8], + ) -> Result { + // 使用 IoGuard 确保 I/O 计数正确管理 + let _io_guard = IoGuard::new(self)?; + + if count == 0 { + return Ok(0); + } + let len = count.checked_mul(LBA_SIZE).ok_or(SystemError::EOVERFLOW)?; + if len > buf.len() { + return Err(SystemError::EINVAL); + } + + let (file_inode, base_offset, limit_end) = { + let inner = self.inner(); + if inner.is_read_only() { + return Err(SystemError::EROFS); + } + let inode = inner.file_inode.clone().ok_or(SystemError::ENODEV)?; + let limit = inner + .offset + .checked_add(inner.file_size) + .ok_or(SystemError::EOVERFLOW)?; + (inode, inner.offset, limit) + }; + + let block_offset = lba_id_start + .checked_mul(LBA_SIZE) + .ok_or(SystemError::EOVERFLOW)?; + let file_offset = base_offset + .checked_add(block_offset) + .ok_or(SystemError::EOVERFLOW)?; + + let end = file_offset.checked_add(len).ok_or(SystemError::EOVERFLOW)?; + if end > limit_end { + return Err(SystemError::ENOSPC); + } + + let data = SpinLock::new(FilePrivateData::Unused); + let data_guard = data.lock(); + + let written = file_inode.write_at(file_offset, len, &buf[..len], data_guard)?; + + if written > 0 { + let _ = self.recalc_effective_size(); + } + + Ok(written) + } + + fn sync(&self) -> Result<(), SystemError> { + Ok(()) + } + + fn blk_size_log2(&self) -> u8 { + 9 + } + + fn as_any_ref(&self) -> &dyn Any { + self + } + + fn device(&self) -> Arc { + self.self_ref.upgrade().unwrap() + } + + fn block_size(&self) -> usize { + LBA_SIZE + } + + fn partitions(&self) -> Vec> { + Vec::new() + } +} diff --git a/kernel/src/driver/block/loop_device/manager.rs b/kernel/src/driver/block/loop_device/manager.rs new file mode 100644 index 000000000..c48aa8361 --- /dev/null +++ b/kernel/src/driver/block/loop_device/manager.rs @@ -0,0 +1,272 @@ +use crate::{ + driver::base::{ + block::{block_device::BlockDevice, manager::block_dev_manager}, + device::DevName, + }, + libs::spinlock::{SpinLock, SpinLockGuard}, +}; +use alloc::{format, sync::Arc}; +use ida::IdAllocator; +use log::info; +use system_error::SystemError; + +use super::{ + constants::{LoopState, LOOP_BASENAME}, + driver::LoopDeviceDriver, + loop_device::LoopDevice, +}; + +/// Loop 设备管理器 +pub struct LoopManager { + inner: SpinLock, +} + +pub struct LoopManagerInner { + devices: [Option>; LoopManager::MAX_DEVICES], + id_alloc: IdAllocator, +} + +impl LoopManager { + /// 最大设备数量 + const MAX_DEVICES: usize = 256; + /// 初始化时创建的设备数量 + const MAX_INIT_DEVICES: usize = 8; + + pub fn new() -> Self { + Self { + inner: SpinLock::new(LoopManagerInner { + devices: [const { None }; Self::MAX_DEVICES], + id_alloc: IdAllocator::new(0, Self::MAX_DEVICES) + .expect("create IdAllocator failed"), + }), + } + } + + fn inner(&'_ self) -> SpinLockGuard<'_, LoopManagerInner> { + self.inner.lock() + } + + #[inline] + fn alloc_id_locked(inner: &mut LoopManagerInner) -> Option { + inner.id_alloc.alloc() + } + + #[inline] + fn alloc_specific_id_locked(inner: &mut LoopManagerInner, id: usize) -> Option { + inner.id_alloc.alloc_specific(id) + } + + #[inline] + fn free_id_locked(inner: &mut LoopManagerInner, id: usize) { + if id < Self::MAX_DEVICES && inner.id_alloc.exists(id) { + inner.id_alloc.free(id); + } + } + + #[inline] + pub fn format_name(id: usize) -> DevName { + DevName::new(format!("{}{}", LOOP_BASENAME, id), id) + } + + fn find_device_by_minor_locked( + inner: &LoopManagerInner, + minor: u32, + ) -> Option> { + if minor >= Self::MAX_DEVICES as u32 { + return None; + } + inner.devices[minor as usize].clone() + } + + /// # 功能 + /// + /// 根据请求的次设备号分配或复用 loop 设备。 + /// + /// ## 参数 + /// + /// - `requested_minor`: 指定的次设备号,`None` 表示自动分配。 + /// + /// ## 返回值 + /// - `Ok(Arc)`: 成功获得的设备。 + /// - `Err(SystemError)`: 无可用设备或参数错误。 + pub fn loop_add(&self, requested_minor: Option) -> Result, SystemError> { + let mut inner = self.inner(); + match requested_minor { + Some(req_minor) => self.loop_add_specific_locked(&mut inner, req_minor), + None => self.loop_add_first_available_locked(&mut inner), + } + } + + /// # 功能 + /// + /// 在锁作用域内分配指定次设备号的 loop 设备。 + /// + /// ## 参数 + /// + /// - `inner`: 管理器内部状态锁。 + /// - `minor`: 目标次设备号。 + /// + /// ## 返回值 + /// - `Ok(Arc)`: 成功获得的设备实例。 + /// - `Err(SystemError)`: 参数无效或设备已被占用。 + fn loop_add_specific_locked( + &self, + inner: &mut LoopManagerInner, + minor: u32, + ) -> Result, SystemError> { + if minor >= Self::MAX_DEVICES as u32 { + return Err(SystemError::EINVAL); + } + + if let Some(device) = Self::find_device_by_minor_locked(inner, minor) { + if device.is_bound() { + return Err(SystemError::EEXIST); + } + return Ok(device); + } + + let id = + Self::alloc_specific_id_locked(inner, minor as usize).ok_or(SystemError::ENOSPC)?; + match self.create_and_register_device_locked(inner, id) { + Ok(device) => Ok(device), + Err(e) => { + Self::free_id_locked(inner, id); + Err(e) + } + } + } + + fn loop_add_first_available_locked( + &self, + inner: &mut LoopManagerInner, + ) -> Result, SystemError> { + if let Some(device) = inner + .devices + .iter() + .flatten() + .find(|device| !device.is_bound()) + { + return Ok(device.clone()); + } + + let id = Self::alloc_id_locked(inner).ok_or(SystemError::ENOSPC)?; + let result = self.create_and_register_device_locked(inner, id); + if result.is_err() { + Self::free_id_locked(inner, id); + } + result + } + + fn create_and_register_device_locked( + &self, + inner: &mut LoopManagerInner, + id: usize, + ) -> Result, SystemError> { + if id >= Self::MAX_DEVICES { + return Err(SystemError::EINVAL); + } + let minor = id as u32; + let devname = Self::format_name(id); + let loop_dev = + LoopDevice::new_empty_loop_device(devname, id, minor).ok_or(SystemError::ENOMEM)?; + + if let Err(e) = block_dev_manager().register(loop_dev.clone()) { + if e == SystemError::EEXIST { + if let Some(existing) = inner.devices[id].clone() { + return Ok(existing); + } + } + return Err(e); + } + + inner.devices[id] = Some(loop_dev.clone()); + log::info!( + "Loop device id {} (minor {}) added successfully.", + id, + minor + ); + Ok(loop_dev) + } + + /// # 功能 + /// + /// 删除指定 minor 的 loop 设备 + /// 实现规范的删除流程,包括状态转换、I/O 排空、资源清理 + /// + /// ## 参数 + /// + /// - `minor`: 要删除的设备的次设备号 + /// + /// ## 返回值 + /// - `Ok(())`: 成功删除设备 + /// - `Err(SystemError)`: 删除失败 + pub fn loop_remove(&self, minor: u32) -> Result<(), SystemError> { + if minor >= Self::MAX_DEVICES as u32 { + return Err(SystemError::EINVAL); + } + let device = { + let inner = self.inner(); + Self::find_device_by_minor_locked(&inner, minor) + } + .ok_or(SystemError::ENODEV)?; + let id = device.id(); + info!("Starting removal of loop device loop{} (id {})", minor, id); + device.enter_rundown_state()?; + let needs_drain = { + let inner = device.inner(); + !matches!(inner.state(), LoopState::Deleting) + }; + + if needs_drain { + device.drain_active_io()?; + } + + device.clear_file()?; + + let _ = device.remove_from_sysfs(); + + let block_dev: Arc = device.clone(); + block_dev_manager().unregister(&block_dev)?; + + { + let mut inner = self.inner(); + inner.devices[minor as usize] = None; + Self::free_id_locked(&mut inner, minor as usize); + } + info!( + "Loop device id {} (minor {}) removed successfully.", + id, minor + ); + Ok(()) + } + + pub fn find_free_minor(&self) -> Option { + let inner = self.inner(); + for minor in 0..Self::MAX_DEVICES as u32 { + match &inner.devices[minor as usize] { + Some(dev) if !dev.is_bound() => return Some(minor), + Some(_) => continue, + None => return Some(minor), + } + } + None + } + + pub fn loop_init(&self, _driver: Arc) -> Result<(), SystemError> { + let mut inner = self.inner(); + for minor in 0..Self::MAX_INIT_DEVICES { + let minor_u32 = minor as u32; + if Self::find_device_by_minor_locked(&inner, minor_u32).is_some() { + continue; + } + let id = + Self::alloc_specific_id_locked(&mut inner, minor).ok_or(SystemError::ENOSPC)?; + if let Err(e) = self.create_and_register_device_locked(&mut inner, id) { + Self::free_id_locked(&mut inner, id); + return Err(e); + } + } + log::info!("Loop devices initialized"); + Ok(()) + } +} diff --git a/kernel/src/driver/block/loop_device/mod.rs b/kernel/src/driver/block/loop_device/mod.rs new file mode 100644 index 000000000..9dabe725d --- /dev/null +++ b/kernel/src/driver/block/loop_device/mod.rs @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 +//! Loop 设备模块 +//! +//! 该模块实现了 Linux 风格的 loop 设备,允许将普通文件作为块设备使用。 +//! +//! # 模块结构 +//! +//! - `constants`: 常量和枚举类型定义 +//! - `loop_device`: Loop 设备实现 +//! - `loop_control`: Loop-control 控制设备实现 +//! - `manager`: Loop 设备管理器 +//! - `driver`: Loop 设备驱动 + +mod constants; +mod driver; +mod loop_control; +#[allow(clippy::module_inception)] +mod loop_device; +mod manager; + +use alloc::sync::Arc; +use system_error::SystemError; + +use crate::{ + driver::base::device::device_register, filesystem::devfs::devfs_register, + init::initcall::INITCALL_DEVICE, +}; +use unified_init::macros::unified_init; + +// 重新导出公共接口 +pub use constants::LOOP_CONTROL_BASENAME; +pub use driver::LoopDeviceDriver; +pub use loop_control::LoopControlDevice; +pub use loop_device::{LoopDevice, LoopPrivateData}; +pub use manager::LoopManager; + +/// 初始化 loop 设备子系统 +#[unified_init(INITCALL_DEVICE)] +pub fn loop_init() -> Result<(), SystemError> { + let loop_mgr = Arc::new(LoopManager::new()); + let driver = LoopDeviceDriver::new(); + let loop_ctl = LoopControlDevice::new(loop_mgr.clone()); + + device_register(loop_ctl.clone())?; + log::info!("Loop control device registered."); + devfs_register(LOOP_CONTROL_BASENAME, loop_ctl.clone())?; + log::info!("Loop control device initialized."); + loop_mgr.loop_init(driver)?; + Ok(()) +} diff --git a/kernel/src/driver/block/mod.rs b/kernel/src/driver/block/mod.rs index 6a91ec54f..b249e6643 100644 --- a/kernel/src/driver/block/mod.rs +++ b/kernel/src/driver/block/mod.rs @@ -1,2 +1,3 @@ pub mod cache; +pub mod loop_device; pub mod virtio_blk; diff --git a/kernel/src/filesystem/devfs/mod.rs b/kernel/src/filesystem/devfs/mod.rs index 00fea08ee..ec9c3d33d 100644 --- a/kernel/src/filesystem/devfs/mod.rs +++ b/kernel/src/filesystem/devfs/mod.rs @@ -207,6 +207,9 @@ impl DevFS { } else if name == "ptmx" { // ptmx设备 dev_root_inode.add_dev(name, device.clone())?; + } else if name == "loop-control" { + // loop-control设备 + dev_root_inode.add_dev(name, device.clone())?; } else { // 在 /dev/char 下创建设备节点 dev_char_inode.add_dev(name, device.clone())?; @@ -243,6 +246,13 @@ impl DevFS { } else if name.starts_with("nvme") { // NVMe设备挂载在 /dev 下 dev_root_inode.add_dev(name, device.clone())?; + } else if name.starts_with("loop") + && name.len() > 4 + && name[4..].chars().all(|c| c.is_ascii_digit()) + { + // loop块设备 (loop0, loop1, ...) 挂载在 /dev 下 + // 注意:不能简单用 starts_with("loop"),因为会与网络 loopback 设备冲突 + dev_root_inode.add_dev(name, device.clone())?; } else { dev_block_inode.add_dev(name, device.clone())?; device.set_parent(Arc::downgrade(&dev_block_inode)); @@ -287,17 +297,26 @@ impl DevFS { dev_char_inode.remove(name)?; } FileType::BlockDevice => { - if dev_root_inode.find("block").is_err() { - return Err(SystemError::ENOENT); - } + // 检查是否是 loop 块设备 (loop0, loop1, ...) + let is_loop_block_device = name.starts_with("loop") + && name.len() > 4 + && name[4..].chars().all(|c| c.is_ascii_digit()); - let any_block_inode = dev_root_inode.find("block")?; - let dev_block_inode = any_block_inode - .as_any_ref() - .downcast_ref::() - .unwrap(); + if is_loop_block_device { + dev_root_inode.remove(name)?; + } else { + if dev_root_inode.find("block").is_err() { + return Err(SystemError::ENOENT); + } - dev_block_inode.remove(name)?; + let any_block_inode = dev_root_inode.find("block")?; + let dev_block_inode = any_block_inode + .as_any_ref() + .downcast_ref::() + .unwrap(); + + dev_block_inode.remove(name)?; + } } _ => { return Err(SystemError::ENOSYS); diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index ddb2c491f..50de198b6 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -15,6 +15,7 @@ use crate::{ arch::MMArch, driver::{ base::{block::SeekFrom, device::DevicePrivateData}, + block::loop_device::LoopPrivateData, tty::tty_device::TtyFilePrivateData, }, filesystem::{ @@ -111,6 +112,8 @@ pub enum FilePrivateData { EPoll(EPollPrivateData), /// pid私有信息 Pid(PidPrivateData), + //loop私有信息 + Loop(LoopPrivateData), /// namespace fd 私有信息(/proc/thread-self/ns/* 打开后得到) Namespace(NamespaceFilePrivateData), /// 不需要文件私有信息 diff --git a/user/apps/c_unitest/test_loop.c b/user/apps/c_unitest/test_loop.c new file mode 100644 index 000000000..e5b148cfb --- /dev/null +++ b/user/apps/c_unitest/test_loop.c @@ -0,0 +1,940 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// =================================================================== +// 测试框架定义 +// =================================================================== + +#define TEST_PASS 0 +#define TEST_FAIL 1 + +// 测试结果统计 +static int g_tests_total = 0; +static int g_tests_passed = 0; +static int g_tests_failed = 0; + +// 测试输出宏 +#define TEST_BEGIN(name) \ + do { \ + g_tests_total++; \ + printf("\n"); \ + printf("========================================\n"); \ + printf("[TEST %d] %s\n", g_tests_total, name); \ + printf("========================================\n"); \ + } while(0) + +#define TEST_END_PASS(name) \ + do { \ + g_tests_passed++; \ + printf("----------------------------------------\n"); \ + printf("[PASS] %s\n", name); \ + printf("----------------------------------------\n"); \ + } while(0) + +#define TEST_END_FAIL(name, reason) \ + do { \ + g_tests_failed++; \ + printf("----------------------------------------\n"); \ + printf("[FAIL] %s: %s\n", name, reason); \ + printf("----------------------------------------\n"); \ + } while(0) + +#define LOG_INFO(fmt, ...) printf("[INFO] " fmt "\n", ##__VA_ARGS__) +#define LOG_ERROR(fmt, ...) fprintf(stderr, "[ERROR] " fmt "\n", ##__VA_ARGS__) +#define LOG_STEP(fmt, ...) printf(" -> " fmt "\n", ##__VA_ARGS__) + +// 打印最终测试汇总 +static void print_test_summary(void) { + printf("\n"); + printf("+========================================+\n"); + printf("| TEST SUMMARY |\n"); + printf("+========================================+\n"); + printf("| Total: %3d |\n", g_tests_total); + printf("| Passed: %3d |\n", g_tests_passed); + printf("| Failed: %3d |\n", g_tests_failed); + printf("+========================================+\n"); + if (g_tests_failed == 0) { + printf("| Result: ALL TESTS PASSED |\n"); + } else { + printf("| Result: SOME TESTS FAILED |\n"); + } + printf("+========================================+\n"); +} + +// =================================================================== +// Loop 设备常量定义 +// =================================================================== + +// 控制命令常量 +#define LOOP_CTL_ADD 0x4C80 +#define LOOP_CTL_REMOVE 0x4C81 +#define LOOP_CTL_GET_FREE 0x4C82 +#define LOOP_SET_FD 0x4C00 +#define LOOP_CLR_FD 0x4C01 +#define LOOP_SET_STATUS64 0x4C04 +#define LOOP_GET_STATUS64 0x4C05 +#define LOOP_CHANGE_FD 0x4C06 +#define LOOP_SET_CAPACITY 0x4C07 + +// 设备路径和测试参数 +#define LOOP_DEVICE_CONTROL "/dev/loop-control" +#define LO_FLAGS_READ_ONLY 0x1 +#define TEST_FILE_NAME "test_image.img" +#define TEST_FILE_NAME_2 "test_image_2.img" +#define TEST_FILE_SIZE (1024 * 1024) // 1MB +#define TEST_FILE_SIZE_2 (512 * 1024) // 512KB + +// Loop 状态结构体 +struct loop_status64 { + uint64_t lo_offset; + uint64_t lo_sizelimit; + uint32_t lo_flags; + uint32_t __pad; +}; + +// =================================================================== +// 全局测试资源 +// =================================================================== + +static int g_control_fd = -1; +static int g_backing_fd_1 = -1; +static int g_backing_fd_2 = -1; + +// =================================================================== +// 辅助函数 +// =================================================================== + +// 创建测试镜像文件 +static int create_test_file(const char *filename, int size) { + LOG_STEP("Creating test file: %s (%d bytes)", filename, size); + + int fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0644); + if (fd < 0) { + LOG_ERROR("Failed to create test file: %s", strerror(errno)); + return -1; + } + + char zero_block[512] = {0}; + for (int i = 0; i < size / 512; ++i) { + if (write(fd, zero_block, 512) != 512) { + LOG_ERROR("Failed to write to test file: %s", strerror(errno)); + close(fd); + return -1; + } + } + + close(fd); + LOG_STEP("Test file created successfully"); + return 0; +} + +// 使用重试机制创建 loop 设备 +static int create_loop_device(int control_fd, int *out_minor) { + for (int retry = 0; retry < 10; retry++) { + // LOOP_CTL_GET_FREE 通过返回值返回 free 的 minor 号 + int free_minor = ioctl(control_fd, LOOP_CTL_GET_FREE, 0); + if (free_minor < 0) { + LOG_ERROR("Failed to get free loop device: %s", strerror(errno)); + return -1; + } + + int ret = ioctl(control_fd, LOOP_CTL_ADD, free_minor); + if (ret >= 0) { + *out_minor = ret; + return 0; + } + + if (errno != EEXIST) { + LOG_ERROR("Failed to add loop device: %s", strerror(errno)); + return -1; + } + // 设备已存在,重试 + } + + LOG_ERROR("Failed to create loop device after 10 retries"); + return -1; +} + +// =================================================================== +// 并发测试辅助结构 +// =================================================================== + +struct io_thread_args { + char loop_dev_path[64]; + int duration_seconds; + volatile int should_stop; + int io_count; + int error_count; +}; + +struct delete_thread_args { + int control_fd; + int loop_minor; + int result; + int error_code; +}; + +static void *io_worker_thread(void *arg) { + struct io_thread_args *args = (struct io_thread_args *)arg; + char buffer[512]; + time_t start_time = time(NULL); + + while (!args->should_stop && (time(NULL) - start_time) < args->duration_seconds) { + int fd = open(args->loop_dev_path, O_RDWR); + if (fd < 0) { + if (errno == ENODEV || errno == ENOENT) { + break; + } + args->error_count++; + usleep(10000); + continue; + } + + if (read(fd, buffer, sizeof(buffer)) < 0) { + if (errno != ENODEV) { + args->error_count++; + } + } else { + args->io_count++; + } + + close(fd); + usleep(1000); + } + + return NULL; +} + +static void *delete_worker_thread(void *arg) { + struct delete_thread_args *args = (struct delete_thread_args *)arg; + usleep(50000); // 50ms 延迟确保 I/O 线程已启动 + + args->result = ioctl(args->control_fd, LOOP_CTL_REMOVE, args->loop_minor); + args->error_code = errno; + + return NULL; +} + +// =================================================================== +// 测试用例 +// =================================================================== + +// 测试 1: 基本读写测试 +static int test_basic_read_write(int loop_fd, const char *loop_path, + struct loop_status64 *status) { + const char *test_name = "Basic Read/Write"; + TEST_BEGIN(test_name); + + char write_buf[512] = "Hello Loop Device!"; + char read_buf[512] = {0}; + char verify_buf[512] = {0}; + + // 写入测试 + LOG_STEP("Writing data to loop device..."); + if (lseek(loop_fd, 0, SEEK_SET) < 0) { + TEST_END_FAIL(test_name, "lseek failed before write"); + return TEST_FAIL; + } + + if (write(loop_fd, write_buf, sizeof(write_buf)) != sizeof(write_buf)) { + TEST_END_FAIL(test_name, "write failed"); + return TEST_FAIL; + } + LOG_STEP("Write successful: '%s'", write_buf); + + // 验证后端文件 + LOG_STEP("Verifying backing file content..."); + int verify_fd = open(TEST_FILE_NAME, O_RDONLY); + if (verify_fd < 0) { + TEST_END_FAIL(test_name, "cannot open backing file for verification"); + return TEST_FAIL; + } + + if (lseek(verify_fd, (off_t)status->lo_offset, SEEK_SET) < 0 || + read(verify_fd, verify_buf, sizeof(write_buf)) != sizeof(write_buf)) { + close(verify_fd); + TEST_END_FAIL(test_name, "cannot read backing file"); + return TEST_FAIL; + } + close(verify_fd); + + if (memcmp(write_buf, verify_buf, sizeof(write_buf)) != 0) { + TEST_END_FAIL(test_name, "backing file content mismatch"); + return TEST_FAIL; + } + LOG_STEP("Backing file verification passed"); + + // 读取测试 + LOG_STEP("Reading data from loop device..."); + if (lseek(loop_fd, 0, SEEK_SET) < 0) { + TEST_END_FAIL(test_name, "lseek failed before read"); + return TEST_FAIL; + } + + if (read(loop_fd, read_buf, sizeof(read_buf)) != sizeof(read_buf)) { + TEST_END_FAIL(test_name, "read failed"); + return TEST_FAIL; + } + LOG_STEP("Read successful: '%s'", read_buf); + + if (strcmp(write_buf, read_buf) != 0) { + TEST_END_FAIL(test_name, "read data mismatch"); + return TEST_FAIL; + } + + TEST_END_PASS(test_name); + return TEST_PASS; +} + +// 测试 2: 只读模式测试 +static int test_read_only_mode(int loop_fd, struct loop_status64 *status) { + const char *test_name = "Read-Only Mode"; + TEST_BEGIN(test_name); + + char write_buf[512] = "Test data"; + + // 设置只读标志 + LOG_STEP("Setting read-only flag..."); + status->lo_flags |= LO_FLAGS_READ_ONLY; + if (ioctl(loop_fd, LOOP_SET_STATUS64, status) < 0) { + TEST_END_FAIL(test_name, "failed to set read-only flag"); + return TEST_FAIL; + } + + // 尝试写入(应该失败) + LOG_STEP("Attempting write in read-only mode (should fail)..."); + errno = 0; + if (lseek(loop_fd, 0, SEEK_SET) < 0) { + // lseek 失败不影响测试 + } + + if (write(loop_fd, write_buf, sizeof(write_buf)) >= 0 || errno != EROFS) { + status->lo_flags &= ~LO_FLAGS_READ_ONLY; + ioctl(loop_fd, LOOP_SET_STATUS64, status); + TEST_END_FAIL(test_name, "write should have failed with EROFS"); + return TEST_FAIL; + } + LOG_STEP("Write correctly rejected with EROFS"); + + // 恢复可写模式 + LOG_STEP("Restoring writable mode..."); + status->lo_flags &= ~LO_FLAGS_READ_ONLY; + if (ioctl(loop_fd, LOOP_SET_STATUS64, status) < 0) { + TEST_END_FAIL(test_name, "failed to restore writable mode"); + return TEST_FAIL; + } + + TEST_END_PASS(test_name); + return TEST_PASS; +} + +// 测试 3: LOOP_CHANGE_FD +static int test_change_fd(int loop_fd, struct loop_status64 *status) { + const char *test_name = "LOOP_CHANGE_FD"; + TEST_BEGIN(test_name); + + char write_buf[512] = "New Backing File Data!"; + char verify_buf[512] = {0}; + + // 切换后端文件 + LOG_STEP("Changing backing file to %s...", TEST_FILE_NAME_2); + if (ioctl(loop_fd, LOOP_CHANGE_FD, g_backing_fd_2) < 0) { + TEST_END_FAIL(test_name, "LOOP_CHANGE_FD failed"); + return TEST_FAIL; + } + LOG_STEP("Backing file changed successfully"); + + // 获取新状态 + struct loop_status64 new_status = {0}; + if (ioctl(loop_fd, LOOP_GET_STATUS64, &new_status) < 0) { + TEST_END_FAIL(test_name, "failed to get status after change"); + return TEST_FAIL; + } + LOG_STEP("New status - offset: %llu, sizelimit: %llu, flags: 0x%x", + (unsigned long long)new_status.lo_offset, + (unsigned long long)new_status.lo_sizelimit, + new_status.lo_flags); + + // 写入新后端文件 + LOG_STEP("Writing to new backing file..."); + if (lseek(loop_fd, 0, SEEK_SET) < 0 || + write(loop_fd, write_buf, sizeof(write_buf)) != sizeof(write_buf)) { + TEST_END_FAIL(test_name, "write to new backing file failed"); + return TEST_FAIL; + } + LOG_STEP("Write successful: '%s'", write_buf); + + // 验证新后端文件内容 + LOG_STEP("Verifying new backing file content..."); + int verify_fd = open(TEST_FILE_NAME_2, O_RDONLY); + if (verify_fd < 0) { + TEST_END_FAIL(test_name, "cannot open new backing file"); + return TEST_FAIL; + } + + if (lseek(verify_fd, (off_t)status->lo_offset, SEEK_SET) < 0 || + read(verify_fd, verify_buf, sizeof(write_buf)) != sizeof(write_buf)) { + close(verify_fd); + TEST_END_FAIL(test_name, "cannot read new backing file"); + return TEST_FAIL; + } + close(verify_fd); + + if (memcmp(write_buf, verify_buf, sizeof(write_buf)) != 0) { + TEST_END_FAIL(test_name, "new backing file content mismatch"); + return TEST_FAIL; + } + + TEST_END_PASS(test_name); + return TEST_PASS; +} + +// 测试 4: LOOP_SET_CAPACITY +static int test_set_capacity(int loop_fd, struct loop_status64 *status) { + const char *test_name = "LOOP_SET_CAPACITY"; + TEST_BEGIN(test_name); + + // 扩大后端文件 + LOG_STEP("Resizing backing file to %d bytes...", TEST_FILE_SIZE_2 * 2); + int resize_fd = open(TEST_FILE_NAME_2, O_RDWR); + if (resize_fd < 0) { + TEST_END_FAIL(test_name, "cannot open backing file for resize"); + return TEST_FAIL; + } + + int new_size = TEST_FILE_SIZE_2 * 2; + if (ftruncate(resize_fd, new_size) < 0) { + close(resize_fd); + TEST_END_FAIL(test_name, "ftruncate failed"); + return TEST_FAIL; + } + close(resize_fd); + LOG_STEP("Backing file resized successfully"); + + // 清除 sizelimit 以便看到扩展效果 + LOG_STEP("Clearing sizelimit..."); + status->lo_sizelimit = 0; + if (ioctl(loop_fd, LOOP_SET_STATUS64, status) < 0) { + TEST_END_FAIL(test_name, "failed to clear sizelimit"); + return TEST_FAIL; + } + + // 调用 LOOP_SET_CAPACITY + LOG_STEP("Calling LOOP_SET_CAPACITY..."); + if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0) { + TEST_END_FAIL(test_name, "LOOP_SET_CAPACITY failed"); + return TEST_FAIL; + } + LOG_STEP("LOOP_SET_CAPACITY successful"); + + // 获取新状态 + struct loop_status64 new_status = {0}; + if (ioctl(loop_fd, LOOP_GET_STATUS64, &new_status) < 0) { + TEST_END_FAIL(test_name, "failed to get status after capacity change"); + return TEST_FAIL; + } + LOG_STEP("New status - offset: %llu, sizelimit: %llu", + (unsigned long long)new_status.lo_offset, + (unsigned long long)new_status.lo_sizelimit); + + // 尝试写入扩展区域 + LOG_STEP("Writing to extended region..."); + char extended_buf[512] = "Extended Data!"; + if (lseek(loop_fd, TEST_FILE_SIZE_2, SEEK_SET) < 0 || + write(loop_fd, extended_buf, sizeof(extended_buf)) != sizeof(extended_buf)) { + TEST_END_FAIL(test_name, "write to extended region failed"); + return TEST_FAIL; + } + LOG_STEP("Write to extended region successful"); + + // 验证扩展区域内容 + LOG_STEP("Verifying extended region content..."); + char verify_buf[512] = {0}; + int verify_fd = open(TEST_FILE_NAME_2, O_RDONLY); + if (verify_fd < 0) { + TEST_END_FAIL(test_name, "cannot open backing file for verification"); + return TEST_FAIL; + } + + off_t verify_offset = (off_t)status->lo_offset + TEST_FILE_SIZE_2; + if (lseek(verify_fd, verify_offset, SEEK_SET) < 0 || + read(verify_fd, verify_buf, sizeof(extended_buf)) != sizeof(extended_buf)) { + close(verify_fd); + TEST_END_FAIL(test_name, "cannot read extended region"); + return TEST_FAIL; + } + close(verify_fd); + + if (memcmp(extended_buf, verify_buf, sizeof(extended_buf)) != 0) { + TEST_END_FAIL(test_name, "extended region content mismatch"); + return TEST_FAIL; + } + + TEST_END_PASS(test_name); + return TEST_PASS; +} + +// 测试 5: 并发 I/O 期间删除设备 +static int test_concurrent_io_deletion(void) { + const char *test_name = "Concurrent I/O During Deletion"; + TEST_BEGIN(test_name); + + #define NUM_IO_THREADS 4 + + // 创建测试用 loop 设备 + int test_minor; + if (create_loop_device(g_control_fd, &test_minor) < 0) { + TEST_END_FAIL(test_name, "failed to create test loop device"); + return TEST_FAIL; + } + LOG_STEP("Created loop device loop%d", test_minor); + + char test_path[64]; + sprintf(test_path, "/dev/loop%d", test_minor); + + int test_fd = open(test_path, O_RDWR); + if (test_fd < 0) { + ioctl(g_control_fd, LOOP_CTL_REMOVE, test_minor); + TEST_END_FAIL(test_name, "failed to open test loop device"); + return TEST_FAIL; + } + + if (ioctl(test_fd, LOOP_SET_FD, g_backing_fd_1) < 0) { + close(test_fd); + ioctl(g_control_fd, LOOP_CTL_REMOVE, test_minor); + TEST_END_FAIL(test_name, "failed to bind test loop device"); + return TEST_FAIL; + } + LOG_STEP("Bound backing file to test device"); + + // 启动 I/O 线程 + pthread_t io_threads[NUM_IO_THREADS]; + struct io_thread_args io_args[NUM_IO_THREADS]; + + LOG_STEP("Starting %d I/O threads...", NUM_IO_THREADS); + for (int i = 0; i < NUM_IO_THREADS; i++) { + strcpy(io_args[i].loop_dev_path, test_path); + io_args[i].duration_seconds = 5; + io_args[i].should_stop = 0; + io_args[i].io_count = 0; + io_args[i].error_count = 0; + + if (pthread_create(&io_threads[i], NULL, io_worker_thread, &io_args[i]) != 0) { + for (int j = 0; j < i; j++) { + io_args[j].should_stop = 1; + pthread_join(io_threads[j], NULL); + } + close(test_fd); + ioctl(g_control_fd, LOOP_CTL_REMOVE, test_minor); + TEST_END_FAIL(test_name, "failed to create I/O thread"); + return TEST_FAIL; + } + } + + close(test_fd); // 关闭主 fd + + // 启动删除线程 + LOG_STEP("Starting deletion thread..."); + pthread_t delete_thread; + struct delete_thread_args delete_args = { + .control_fd = g_control_fd, + .loop_minor = test_minor, + .result = 0, + .error_code = 0 + }; + + if (pthread_create(&delete_thread, NULL, delete_worker_thread, &delete_args) != 0) { + for (int i = 0; i < NUM_IO_THREADS; i++) { + io_args[i].should_stop = 1; + pthread_join(io_threads[i], NULL); + } + ioctl(g_control_fd, LOOP_CTL_REMOVE, test_minor); + TEST_END_FAIL(test_name, "failed to create delete thread"); + return TEST_FAIL; + } + + // 等待删除完成 + pthread_join(delete_thread, NULL); + LOG_STEP("Deletion completed with result: %d (errno: %d)", + delete_args.result, delete_args.error_code); + + // 停止并等待 I/O 线程 + for (int i = 0; i < NUM_IO_THREADS; i++) { + io_args[i].should_stop = 1; + } + + int total_io = 0, total_errors = 0; + for (int i = 0; i < NUM_IO_THREADS; i++) { + pthread_join(io_threads[i], NULL); + total_io += io_args[i].io_count; + total_errors += io_args[i].error_count; + } + LOG_STEP("I/O statistics: %d successful, %d errors", total_io, total_errors); + + // 验证设备已删除 + int verify_fd = open(test_path, O_RDWR); + if (verify_fd >= 0) { + close(verify_fd); + TEST_END_FAIL(test_name, "device still accessible after deletion"); + return TEST_FAIL; + } + + if (delete_args.result != 0) { + TEST_END_FAIL(test_name, "deletion failed"); + return TEST_FAIL; + } + + TEST_END_PASS(test_name); + return TEST_PASS; + + #undef NUM_IO_THREADS +} + +// 测试 6: 删除未绑定的设备 +static int test_delete_unbound_device(void) { + const char *test_name = "Delete Unbound Device"; + TEST_BEGIN(test_name); + + int minor; + if (create_loop_device(g_control_fd, &minor) < 0) { + TEST_END_FAIL(test_name, "failed to create loop device"); + return TEST_FAIL; + } + LOG_STEP("Created unbound loop device loop%d", minor); + + // 立即删除 + LOG_STEP("Deleting unbound device..."); + if (ioctl(g_control_fd, LOOP_CTL_REMOVE, minor) < 0) { + TEST_END_FAIL(test_name, "failed to delete unbound device"); + return TEST_FAIL; + } + + TEST_END_PASS(test_name); + return TEST_PASS; +} + +// 测试 7: 重复删除设备 +static int test_duplicate_deletion(void) { + const char *test_name = "Duplicate Deletion"; + TEST_BEGIN(test_name); + + int minor; + if (create_loop_device(g_control_fd, &minor) < 0) { + TEST_END_FAIL(test_name, "failed to create loop device"); + return TEST_FAIL; + } + LOG_STEP("Created loop device loop%d", minor); + + // 第一次删除 + LOG_STEP("First deletion..."); + if (ioctl(g_control_fd, LOOP_CTL_REMOVE, minor) < 0) { + TEST_END_FAIL(test_name, "first deletion failed"); + return TEST_FAIL; + } + LOG_STEP("First deletion successful"); + + // 第二次删除(应该失败) + LOG_STEP("Second deletion (should fail)..."); + errno = 0; + int ret = ioctl(g_control_fd, LOOP_CTL_REMOVE, minor); + if (ret >= 0) { + TEST_END_FAIL(test_name, "second deletion should have failed"); + return TEST_FAIL; + } + + if (errno != ENODEV && errno != EINVAL) { + char msg[64]; + snprintf(msg, sizeof(msg), "unexpected errno: %d", errno); + TEST_END_FAIL(test_name, msg); + return TEST_FAIL; + } + LOG_STEP("Second deletion correctly failed with errno %d", errno); + + TEST_END_PASS(test_name); + return TEST_PASS; +} + +// 测试 8: 文件描述符泄漏检测 +static int test_fd_leak_detection(void) { + const char *test_name = "FD Leak Detection"; + TEST_BEGIN(test_name); + + #define LEAK_TEST_COUNT 10 + + int minors[LEAK_TEST_COUNT]; + int fds[LEAK_TEST_COUNT]; + + LOG_STEP("Creating %d loop devices...", LEAK_TEST_COUNT); + for (int i = 0; i < LEAK_TEST_COUNT; i++) { + minors[i] = -1; + fds[i] = -1; + + if (create_loop_device(g_control_fd, &minors[i]) < 0) { + // 清理已创建的设备 + for (int j = 0; j < i; j++) { + if (fds[j] >= 0) { + ioctl(fds[j], LOOP_CLR_FD, 0); + close(fds[j]); + } + if (minors[j] >= 0) ioctl(g_control_fd, LOOP_CTL_REMOVE, minors[j]); + } + TEST_END_FAIL(test_name, "failed to create loop device"); + return TEST_FAIL; + } + + char path[64]; + sprintf(path, "/dev/loop%d", minors[i]); + fds[i] = open(path, O_RDWR); + if (fds[i] < 0) { + // 清理已创建的设备 + for (int j = 0; j < i; j++) { + if (fds[j] >= 0) { + ioctl(fds[j], LOOP_CLR_FD, 0); + close(fds[j]); + } + if (minors[j] >= 0) ioctl(g_control_fd, LOOP_CTL_REMOVE, minors[j]); + } + ioctl(g_control_fd, LOOP_CTL_REMOVE, minors[i]); + TEST_END_FAIL(test_name, "failed to open loop device"); + return TEST_FAIL; + } + + // 立即绑定后端文件,这样下次 GET_FREE 会返回不同的 minor + if (ioctl(fds[i], LOOP_SET_FD, g_backing_fd_1) < 0) { + // 清理已创建的设备 + for (int j = 0; j <= i; j++) { + if (fds[j] >= 0) { + ioctl(fds[j], LOOP_CLR_FD, 0); + close(fds[j]); + } + if (minors[j] >= 0) ioctl(g_control_fd, LOOP_CTL_REMOVE, minors[j]); + } + TEST_END_FAIL(test_name, "failed to bind loop device"); + return TEST_FAIL; + } + } + LOG_STEP("Created %d devices successfully", LEAK_TEST_COUNT); + + // 删除所有设备 + LOG_STEP("Deleting all devices..."); + int success_count = 0; + for (int i = 0; i < LEAK_TEST_COUNT; i++) { + if (fds[i] >= 0) { + ioctl(fds[i], LOOP_CLR_FD, 0); + close(fds[i]); + } + if (ioctl(g_control_fd, LOOP_CTL_REMOVE, minors[i]) == 0) { + success_count++; + } + } + + LOG_STEP("Deleted %d/%d devices", success_count, LEAK_TEST_COUNT); + + if (success_count != LEAK_TEST_COUNT) { + TEST_END_FAIL(test_name, "not all devices deleted"); + return TEST_FAIL; + } + + TEST_END_PASS(test_name); + return TEST_PASS; + + #undef LEAK_TEST_COUNT +} + +// 测试 9: 删除后设备不可访问 +static int test_device_inaccessible_after_deletion(void) { + const char *test_name = "Device Inaccessible After Deletion"; + TEST_BEGIN(test_name); + + int minor; + if (create_loop_device(g_control_fd, &minor) < 0) { + TEST_END_FAIL(test_name, "failed to create loop device"); + return TEST_FAIL; + } + + char path[64]; + sprintf(path, "/dev/loop%d", minor); + LOG_STEP("Created loop device %s", path); + + int fd = open(path, O_RDWR); + if (fd < 0) { + ioctl(g_control_fd, LOOP_CTL_REMOVE, minor); + TEST_END_FAIL(test_name, "failed to open loop device"); + return TEST_FAIL; + } + + if (ioctl(fd, LOOP_SET_FD, g_backing_fd_1) < 0) { + close(fd); + ioctl(g_control_fd, LOOP_CTL_REMOVE, minor); + TEST_END_FAIL(test_name, "failed to bind loop device"); + return TEST_FAIL; + } + LOG_STEP("Bound backing file"); + + // 执行一次成功的 I/O + char buf[512] = "Test data"; + if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { + close(fd); + ioctl(g_control_fd, LOOP_CTL_REMOVE, minor); + TEST_END_FAIL(test_name, "initial write failed"); + return TEST_FAIL; + } + LOG_STEP("Initial I/O successful"); + + close(fd); + + // 删除设备 + LOG_STEP("Deleting device..."); + if (ioctl(g_control_fd, LOOP_CTL_REMOVE, minor) < 0) { + TEST_END_FAIL(test_name, "deletion failed"); + return TEST_FAIL; + } + + // 尝试重新打开 + LOG_STEP("Attempting to reopen deleted device..."); + errno = 0; + int reopen_fd = open(path, O_RDWR); + if (reopen_fd >= 0) { + close(reopen_fd); + TEST_END_FAIL(test_name, "device still accessible after deletion"); + return TEST_FAIL; + } + + if (errno != ENODEV && errno != ENOENT) { + char msg[64]; + snprintf(msg, sizeof(msg), "unexpected errno: %d", errno); + TEST_END_FAIL(test_name, msg); + return TEST_FAIL; + } + LOG_STEP("Device correctly inaccessible (errno: %d)", errno); + + TEST_END_PASS(test_name); + return TEST_PASS; +} + +// =================================================================== +// 主函数 +// =================================================================== + +int main(void) { + printf("+========================================+\n"); + printf("| Loop Device Test Suite |\n"); + printf("+========================================+\n"); + + // 初始化测试环境 + LOG_INFO("Initializing test environment..."); + + if (create_test_file(TEST_FILE_NAME, TEST_FILE_SIZE) < 0 || + create_test_file(TEST_FILE_NAME_2, TEST_FILE_SIZE_2) < 0) { + LOG_ERROR("Failed to create test files"); + return EXIT_FAILURE; + } + + g_backing_fd_1 = open(TEST_FILE_NAME, O_RDWR); + g_backing_fd_2 = open(TEST_FILE_NAME_2, O_RDWR); + if (g_backing_fd_1 < 0 || g_backing_fd_2 < 0) { + LOG_ERROR("Failed to open backing files"); + goto cleanup_files; + } + + g_control_fd = open(LOOP_DEVICE_CONTROL, O_RDWR); + if (g_control_fd < 0) { + LOG_ERROR("Failed to open loop control device: %s", strerror(errno)); + goto cleanup_backing; + } + LOG_INFO("Test environment initialized"); + + // 创建主测试 loop 设备 + int main_minor; + if (create_loop_device(g_control_fd, &main_minor) < 0) { + LOG_ERROR("Failed to create main loop device"); + goto cleanup_control; + } + + char main_loop_path[64]; + sprintf(main_loop_path, "/dev/loop%d", main_minor); + LOG_INFO("Created main loop device: %s", main_loop_path); + + int main_loop_fd = open(main_loop_path, O_RDWR); + if (main_loop_fd < 0) { + LOG_ERROR("Failed to open main loop device"); + ioctl(g_control_fd, LOOP_CTL_REMOVE, main_minor); + goto cleanup_control; + } + + if (ioctl(main_loop_fd, LOOP_SET_FD, g_backing_fd_1) < 0) { + LOG_ERROR("Failed to bind main loop device"); + close(main_loop_fd); + ioctl(g_control_fd, LOOP_CTL_REMOVE, main_minor); + goto cleanup_control; + } + + // 配置偏移和大小限制 + struct loop_status64 status = { + .lo_offset = 512, + .lo_sizelimit = TEST_FILE_SIZE - 512, + .lo_flags = 0, + .__pad = 0 + }; + + if (ioctl(main_loop_fd, LOOP_SET_STATUS64, &status) < 0) { + LOG_ERROR("Failed to configure main loop device"); + close(main_loop_fd); + ioctl(g_control_fd, LOOP_CTL_REMOVE, main_minor); + goto cleanup_control; + } + LOG_INFO("Main loop device configured (offset: %llu, sizelimit: %llu)", + (unsigned long long)status.lo_offset, + (unsigned long long)status.lo_sizelimit); + + // =================================================================== + // 运行测试用例 + // =================================================================== + + // 基本功能测试 + test_basic_read_write(main_loop_fd, main_loop_path, &status); + test_read_only_mode(main_loop_fd, &status); + test_change_fd(main_loop_fd, &status); + test_set_capacity(main_loop_fd, &status); + + // 资源回收测试 + test_concurrent_io_deletion(); + test_delete_unbound_device(); + test_duplicate_deletion(); + test_fd_leak_detection(); + test_device_inaccessible_after_deletion(); + + // =================================================================== + // 清理 + // =================================================================== + + LOG_INFO("Cleaning up main loop device..."); + ioctl(main_loop_fd, LOOP_CLR_FD, 0); + close(main_loop_fd); + ioctl(g_control_fd, LOOP_CTL_REMOVE, main_minor); + +cleanup_control: + if (g_control_fd >= 0) close(g_control_fd); + +cleanup_backing: + if (g_backing_fd_1 >= 0) close(g_backing_fd_1); + if (g_backing_fd_2 >= 0) close(g_backing_fd_2); + +cleanup_files: + unlink(TEST_FILE_NAME); + unlink(TEST_FILE_NAME_2); + + // 打印测试汇总 + print_test_summary(); + + return (g_tests_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +}