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}