os/vfs/
inode.rs

1//! Inode 抽象层 - VFS 存储层接口
2//!
3//! 该模块定义了文件系统的底层存储接口,提供无状态的文件和目录访问能力。
4//!
5//! # 组件
6//!
7//! - [`Inode`] trait:文件/目录的核心操作接口
8//! - [`InodeMetadata`]:文件元数据(大小、权限、时间戳等)
9//! - [`InodeType`]:文件类型枚举(普通文件、目录、符号链接等)
10//! - [`FileMode`]:POSIX 兼容的权限位
11//! - [`DirEntry`]:轻量级目录项(用于 readdir)
12//!
13//! # 与 File trait 的关系
14//!
15//! | 方面 | File (会话层) | Inode (存储层) |
16//! |------|---------------|----------------|
17//! | 状态 | 有状态 (offset、flags) | 无状态 |
18//! | 方法 | `read(buf)` | `read_at(offset, buf)` |
19//! | 实例 | 每次 open 创建新实例 | 多个 File 可共享同一 Inode |
20//! | 职责 | 维护会话状态 | 提供存储访问 |
21//!
22//! ## 为什么 Inode 是无状态的?
23//!
24//! 无状态设计使得:
25//! - 同一文件可被多次打开,各自维护独立的 offset
26//! - 硬链接可以共享同一个 Inode
27//! - 多线程读写时无需锁定 Inode(offset 由 File 管理)
28//!
29//! ```text
30//! File { offset: 0 }  ──┐
31//!                       ├──> Inode { data: [...] }
32//! File { offset: 100 }──┘
33//! ```
34//!
35//! # 设计要点
36//!
37//! ## 随机访问
38//!
39//! 所有读写方法都携带 `offset` 参数,支持真正的随机访问:
40//!
41//! ```rust
42//! // 可以乱序访问,不影响彼此
43//! inode.read_at(100, &mut buf1)?;  // 读取位置 100
44//! inode.read_at(0, &mut buf2)?;    // 读取位置 0
45//! ```
46//!
47//! ## 幂等性
48//!
49//! 读取操作应该是幂等的(多次调用返回相同结果):
50//!
51//! ```rust
52//! let n1 = inode.read_at(0, &mut buf)?;
53//! let n2 = inode.read_at(0, &mut buf)?;
54//! assert_eq!(n1, n2);  // 相同的读取结果
55//! ```
56//!
57//! ## 目录操作
58//!
59//! Inode 不仅支持文件读写,还支持目录操作:
60//!
61//! - `lookup(name)` - 查找子项
62//! - `create(name)` - 创建文件
63//! - `mkdir(name)` - 创建目录
64//! - `unlink(name)` - 删除文件
65//! - `readdir()` - 列出目录内容
66//!
67//! # 使用示例
68//!
69//! ```rust
70//! use vfs::{Inode, FileMode};
71//!
72//! // 1. 读写文件
73//! let mut buf = [0u8; 512];
74//! let n = inode.read_at(0, &mut buf)?;
75//! inode.write_at(512, b"hello")?;
76//!
77//! // 2. 目录操作
78//! let child_inode = parent_inode.lookup("file.txt")?;
79//! parent_inode.create("new.txt", FileMode::S_IFREG | FileMode::S_IRUSR)?;
80//!
81//! // 3. 获取元数据
82//! let metadata = inode.metadata()?;
83//! println!("大小: {}, 类型: {:?}", metadata.size, metadata.inode_type);
84//! ```
85
86use core::any::Any;
87
88use crate::uapi::time::TimeSpec;
89use crate::vfs::{Dentry, FsError};
90use alloc::string::String;
91use alloc::sync::Arc;
92use alloc::sync::Weak;
93use alloc::vec::Vec;
94
95/// 文件类型
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub enum InodeType {
98    File,        // 普通文件
99    Directory,   // 目录
100    Symlink,     // 符号链接
101    CharDevice,  // 字符设备
102    BlockDevice, // 块设备
103    Fifo,        // 命名管道
104    Socket,      // 套接字
105}
106
107bitflags::bitflags! {
108    #[derive(Debug, Clone, Copy)]
109    /// 文件权限和类型(与 POSIX 兼容)
110    pub struct FileMode: u32 {
111        // 文件类型掩码
112        const S_IFMT   = 0o170000;  // 文件类型掩码
113        const S_IFREG  = 0o100000;  // 普通文件
114        const S_IFDIR  = 0o040000;  // 目录
115        const S_IFLNK  = 0o120000;  // 符号链接
116        const S_IFCHR  = 0o020000;  // 字符设备
117        const S_IFBLK  = 0o060000;  // 块设备
118        const S_IFIFO  = 0o010000;  // FIFO
119        const S_IFSOCK = 0o140000;  // Socket
120
121        // 用户权限
122        const S_IRUSR  = 0o400;     // 用户读
123        const S_IWUSR  = 0o200;     // 用户写
124        const S_IXUSR  = 0o100;     // 用户执行
125
126        // 组权限
127        const S_IRGRP  = 0o040;     // 组读
128        const S_IWGRP  = 0o020;     // 组写
129        const S_IXGRP  = 0o010;     // 组执行
130
131        // 其他用户权限
132        const S_IROTH  = 0o004;     // 其他读
133        const S_IWOTH  = 0o002;     // 其他写
134        const S_IXOTH  = 0o001;     // 其他执行
135
136        // 特殊位
137        const S_ISUID  = 0o4000;    // Set UID
138        const S_ISGID  = 0o2000;    // Set GID
139        const S_ISVTX  = 0o1000;    // Sticky bit
140    }
141}
142
143impl FileMode {
144    /// 检查是否有读权限(暂时只检查用户权限)
145    pub fn can_read(&self) -> bool {
146        self.contains(FileMode::S_IRUSR)
147    }
148
149    /// 检查是否有写权限
150    pub fn can_write(&self) -> bool {
151        self.contains(FileMode::S_IWUSR)
152    }
153
154    /// 检查是否有执行权限
155    pub fn can_execute(&self) -> bool {
156        self.contains(FileMode::S_IXUSR)
157    }
158}
159
160/// 轻量级目录项(readdir 返回)
161///
162/// 用于数据传输,无引用关系,读取后即可丢弃
163#[derive(Debug, Clone)]
164pub struct DirEntry {
165    pub name: String,          // 文件名
166    pub inode_no: usize,       // Inode 编号
167    pub inode_type: InodeType, // 文件类型
168}
169
170/// 文件元数据
171#[derive(Debug, Clone)]
172pub struct InodeMetadata {
173    pub inode_no: usize,       // Inode 编号
174    pub inode_type: InodeType, // 文件类型
175    pub mode: FileMode,        // 权限位
176    pub uid: u32,              // 用户 ID
177    pub gid: u32,              // 组 ID
178    pub size: usize,           // 文件大小(字节)
179    pub atime: TimeSpec,       // 访问时间
180    pub mtime: TimeSpec,       // 修改时间
181    pub ctime: TimeSpec,       // 状态改变时间
182    pub nlinks: usize,         // 硬链接数
183    pub blocks: usize,         // 占用的块数(512B 为单位)
184    pub rdev: u64,             // 设备号(仅对 CharDevice 和 BlockDevice 有效)
185}
186
187/// 文件系统底层存储接口
188///
189/// Inode 代表文件系统中的一个文件或目录,提供无状态的随机访问。
190///
191/// # 设计要点
192///
193/// - 所有读写方法必须携带 `offset` 参数(体现随机访问能力)
194/// - 不维护会话状态(offset 由上层 File 维护)
195/// - 支持目录操作(lookup、create、mkdir、unlink)
196pub trait Inode: Send + Sync + Any {
197    /// 获取文件元数据
198    fn metadata(&self) -> Result<InodeMetadata, FsError>;
199
200    /// 从指定偏移量读取数据
201    ///
202    /// 多次调用相同参数应返回相同结果(无副作用)。
203    fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize, FsError>;
204
205    /// 向指定偏移量写入数据
206    fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize, FsError>;
207
208    /// 在目录中查找子项
209    ///
210    /// 返回子项的 Inode。仅对目录有效。
211    fn lookup(&self, name: &str) -> Result<Arc<dyn Inode>, FsError>;
212
213    /// 在目录中创建文件
214    fn create(&self, name: &str, mode: FileMode) -> Result<Arc<dyn Inode>, FsError>;
215
216    /// 在目录中创建子目录
217    fn mkdir(&self, name: &str, mode: FileMode) -> Result<Arc<dyn Inode>, FsError>;
218
219    /// 创建符号链接
220    fn symlink(&self, name: &str, target: &str) -> Result<Arc<dyn Inode>, FsError>;
221
222    /// 创建硬链接
223    fn link(&self, name: &str, target: &Arc<dyn Inode>) -> Result<(), FsError>;
224
225    /// 删除普通文件/链接
226    fn unlink(&self, name: &str) -> Result<(), FsError>;
227
228    /// 删除目录
229    fn rmdir(&self, name: &str) -> Result<(), FsError>;
230
231    /// 重命名/移动 (原子操作)
232    fn rename(
233        &self,
234        old_name: &str,
235        new_parent: Arc<dyn Inode>,
236        new_name: &str,
237    ) -> Result<(), FsError>;
238
239    /// 列出目录内容
240    fn readdir(&self) -> Result<Vec<DirEntry>, FsError>;
241
242    /// 截断文件到指定大小
243    fn truncate(&self, size: usize) -> Result<(), FsError>;
244
245    /// 同步文件数据到存储设备
246    fn sync(&self) -> Result<(), FsError>;
247
248    /// 设置 Dentry(可选方法)
249    fn set_dentry(&self, _dentry: Weak<Dentry>) {}
250
251    /// 获取 Dentry(可选方法)
252    fn get_dentry(&self) -> Option<Arc<Dentry>> {
253        None
254    }
255
256    /// 向下转型为 &dyn Any,用于支持 downcast
257    fn as_any(&self) -> &dyn Any;
258
259    /// 设置文件时间戳
260    fn set_times(&self, atime: Option<TimeSpec>, mtime: Option<TimeSpec>) -> Result<(), FsError>;
261
262    /// 读取符号链接的目标路径
263    fn readlink(&self) -> Result<String, FsError>;
264
265    /// 创建设备文件节点
266    fn mknod(&self, name: &str, mode: FileMode, dev: u64) -> Result<Arc<dyn Inode>, FsError>;
267
268    /// 修改文件所有者和组
269    ///
270    /// # 参数
271    /// * `uid` - 新的用户 ID(`u32::MAX` 表示不改变)
272    /// * `gid` - 新的组 ID(`u32::MAX` 表示不改变)
273    ///
274    /// # 返回值
275    /// * `Ok(())` - 成功
276    /// * `Err(FsError)` - 失败
277    ///
278    /// # 在单 root 用户系统中的行为
279    /// 此方法会更新 inode 的 uid/gid 字段,但不进行权限检查。
280    /// 所有调用都会成功(除非文件系统错误)。
281    fn chown(&self, _uid: u32, _gid: u32) -> Result<(), FsError>;
282
283    /// 修改文件权限模式
284    ///
285    /// # 参数
286    /// * `mode` - 新的权限模式(只修改权限位,不修改文件类型位)
287    ///
288    /// # 返回值
289    /// * `Ok(())` - 成功
290    /// * `Err(FsError)` - 失败
291    ///
292    /// # 在单 root 用户系统中的行为
293    /// 此方法会更新 inode 的 mode 字段,但不进行权限检查。
294    /// 所有调用都会成功(除非文件系统错误)。
295    fn chmod(&self, _mode: FileMode) -> Result<(), FsError>;
296}
297
298/// 为 `Arc<dyn Inode>` 提供向下转型辅助方法
299impl dyn Inode {
300    /// 尝试向下转型为具体的 Inode 类型
301    pub fn downcast_arc<T: Inode>(self: Arc<Self>) -> Result<Arc<T>, Arc<Self>> {
302        if (*self).as_any().is::<T>() {
303            // SAFETY: 已经通过 is::<T>() 检查了类型
304            unsafe {
305                let ptr = Arc::into_raw(self);
306                Ok(Arc::from_raw(ptr as *const T))
307            }
308        } else {
309            Err(self)
310        }
311    }
312
313    /// 尝试获取具体类型的引用
314    pub fn downcast_ref<T: Inode>(&self) -> Option<&T> {
315        self.as_any().downcast_ref::<T>()
316    }
317}