os/uapi/fs.rs
1//! 文件系统相关的用户空间 API 定义
2
3use bitflags::bitflags;
4
5/// 文件系统类型
6///
7/// 使用枚举避免魔数,同时保持与 Linux 魔数兼容
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[repr(i64)]
10pub enum FileSystemType {
11 /// EXT2/3/4 文件系统
12 /// 魔数来自:include/uapi/linux/magic.h
13 Ext4 = 0xEF53,
14
15 /// 未知或不支持的文件系统
16 Unknown = 0,
17}
18
19impl FileSystemType {
20 /// 从文件系统类型字符串获取枚举值
21 ///
22 /// # 示例
23 /// ```
24 /// let fs_type = FileSystemType::from_str("ext4");
25 /// assert_eq!(fs_type, FileSystemType::Ext4);
26 /// ```
27 pub fn from_str(fs_type: &str) -> Self {
28 match fs_type {
29 "ext4" | "ext3" | "ext2" => Self::Ext4,
30 _ => Self::Unknown,
31 }
32 }
33
34 /// 获取文件系统魔数
35 ///
36 /// # 返回值
37 /// 返回 Linux 定义的文件系统魔数(i64)
38 #[inline]
39 pub fn magic(self) -> i64 {
40 self as i64
41 }
42}
43
44bitflags! {
45 /// 文件系统挂载标志(用于 VFS 内部)
46 ///
47 /// 参考:include/uapi/linux/mount.h
48 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
49 pub struct MountFlags: i64 {
50 /// 只读挂载
51 const RDONLY = 1;
52
53 /// 忽略 suid/sgid 位
54 const NOSUID = 2;
55
56 /// 禁止访问设备文件
57 const NODEV = 4;
58
59 /// 禁止执行程序
60 const NOEXEC = 8;
61
62 /// 同步写入
63 const SYNCHRONOUS = 16;
64
65 /// 不更新访问时间
66 const NOATIME = 1024;
67
68 /// 不更新目录访问时间
69 const NODIRATIME = 2048;
70
71 /// 相对访问时间(修正值:与 Linux 内核一致)
72 const RELATIME = 2097152;
73 }
74}
75
76bitflags! {
77 /// mount 系统调用标志位(与 Linux ABI 完全一致)
78 ///
79 /// 参考:include/uapi/linux/mount.h
80 /// 这些标志在当前实现中会被忽略,但保留以保持 ABI 兼容性
81 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
82 pub struct SysMountFlags: u64 {
83 /// 只读挂载
84 const MS_RDONLY = 1;
85
86 /// 禁止 SUID/SGID 位
87 const MS_NOSUID = 2;
88
89 /// 禁止访问设备文件
90 const MS_NODEV = 4;
91
92 /// 禁止执行程序
93 const MS_NOEXEC = 8;
94
95 /// 同步所有写入
96 const MS_SYNCHRONOUS = 16;
97
98 /// 重新挂载(修改挂载选项)
99 const MS_REMOUNT = 32;
100
101 /// 允许强制锁
102 const MS_MANDLOCK = 64;
103
104 /// 目录同步
105 const MS_DIRSYNC = 128;
106
107 /// 不跟随符号链接
108 const MS_NOSYMFOLLOW = 256;
109
110 /// 不更新访问时间
111 const MS_NOATIME = 1024;
112
113 /// 不更新目录访问时间
114 const MS_NODIRATIME = 2048;
115
116 /// 绑定挂载
117 const MS_BIND = 4096;
118
119 /// 移动挂载
120 const MS_MOVE = 8192;
121
122 /// 递归操作
123 const MS_REC = 16384;
124
125 /// 相对访问时间
126 const MS_RELATIME = 2097152;
127
128 /// 严格的访问时间
129 const MS_STRICTATIME = 16777216;
130
131 /// 延迟更新时间
132 const MS_LAZYTIME = 33554432;
133 }
134}
135
136bitflags! {
137 /// umount2 系统调用标志位(与 Linux ABI 完全一致)
138 ///
139 /// 参考:include/uapi/linux/mount.h
140 /// 这些标志在当前实现中会被忽略,但保留以保持 ABI 兼容性
141 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
142 pub struct UmountFlags: i32 {
143 /// 强制卸载(即使正在使用)
144 const MNT_FORCE = 1;
145
146 /// 延迟卸载(不再使用时卸载)
147 const MNT_DETACH = 2;
148
149 /// 仅当挂载点过期时卸载
150 const MNT_EXPIRE = 4;
151
152 /// 不追踪符号链接
153 const UMOUNT_NOFOLLOW = 8;
154 }
155}
156
157bitflags! {
158 /// 访问模式标志
159 ///
160 /// 用于 `faccessat` 系统调用
161 /// 参考:include/uapi/asm-generic/fcntl.h
162 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
163 pub struct AccessMode: i32 {
164 /// 文件存在性测试(F_OK)
165 const EXISTS = 0;
166
167 /// 可执行性测试(X_OK)
168 const EXECUTE = 1;
169
170 /// 可写性测试(W_OK)
171 const WRITE = 2;
172
173 /// 可读性测试(R_OK)
174 const READ = 4;
175 }
176}
177
178// 兼容 Linux 常量名
179pub const F_OK: i32 = 0;
180pub const X_OK: i32 = 1;
181pub const W_OK: i32 = 2;
182pub const R_OK: i32 = 4;
183
184bitflags! {
185 /// AT_* 标志
186 ///
187 /// 用于 `*at` 系列系统调用(openat, fstatat, etc.)
188 /// 参考:include/uapi/linux/fcntl.h
189 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
190 pub struct AtFlags: u32 {
191 /// 不跟随符号链接(AT_SYMLINK_NOFOLLOW)
192 const SYMLINK_NOFOLLOW = 0x100;
193
194 /// 使用有效 UID/GID 而非实际 UID/GID(AT_EACCESS)
195 const EACCESS = 0x200;
196
197 /// 移除目录(AT_REMOVEDIR,用于 unlinkat)
198 const REMOVEDIR = 0x200;
199
200 /// 路径为空时操作 dirfd 本身(AT_EMPTY_PATH)
201 const EMPTY_PATH = 0x1000;
202
203 /// 不触发自动挂载(AT_NO_AUTOMOUNT)
204 const NO_AUTOMOUNT = 0x800;
205 }
206}
207
208/// AT_FDCWD 常量
209///
210/// 表示使用当前工作目录作为相对路径的基准
211pub const AT_FDCWD: i32 = -100;
212
213bitflags! {
214 /// renameat2 标志
215 ///
216 /// 参考:include/uapi/linux/fs.h
217 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
218 pub struct RenameFlags: u32 {
219 /// 目标存在时失败(RENAME_NOREPLACE)
220 const NOREPLACE = 1 << 0;
221
222 /// 原子交换两个文件(RENAME_EXCHANGE)
223 const EXCHANGE = 1 << 1;
224
225 /// 创建 whiteout 对象(RENAME_WHITEOUT,Union FS)
226 const WHITEOUT = 1 << 2;
227 }
228}
229
230impl RenameFlags {
231 /// 检查标志组合是否合法
232 ///
233 /// NOREPLACE 和 EXCHANGE 不能同时设置
234 pub fn is_valid(self) -> bool {
235 !(self.contains(Self::NOREPLACE) && self.contains(Self::EXCHANGE))
236 }
237}
238
239/// Linux statfs/statfs64 结构体(RISC-V 64位)
240///
241/// **重要**: 此结构体必须与 Linux 内核定义完全一致
242///
243/// 参考:include/uapi/asm-generic/statfs.h
244///
245/// # 字段说明
246/// - `f_type`: 文件系统类型魔数(使用 FileSystemType 枚举)
247/// - `f_bsize`: 最优传输块大小
248/// - `f_blocks`: 文件系统总块数
249/// - `f_bfree`: 空闲块数
250/// - `f_bavail`: 非特权用户可用块数
251/// - `f_files`: 总 inode 数
252/// - `f_ffree`: 空闲 inode 数
253/// - `f_fsid`: 文件系统 ID
254/// - `f_namelen`: 最大文件名长度
255/// - `f_frsize`: 片段大小
256/// - `f_flags`: 挂载标志(MountFlags)
257/// - `f_spare`: 保留字段(填充)
258#[repr(C)]
259#[derive(Debug, Clone, Copy)]
260pub struct LinuxStatFs {
261 pub f_type: i64,
262 pub f_bsize: i64,
263 pub f_blocks: u64,
264 pub f_bfree: u64,
265 pub f_bavail: u64,
266 pub f_files: u64,
267 pub f_ffree: u64,
268 pub f_fsid: [i32; 2],
269 pub f_namelen: i64,
270 pub f_frsize: i64,
271 pub f_flags: i64,
272 pub f_spare: [i64; 4],
273}
274
275impl LinuxStatFs {
276 /// 创建零初始化的 statfs 结构
277 pub const fn zeroed() -> Self {
278 Self {
279 f_type: 0,
280 f_bsize: 0,
281 f_blocks: 0,
282 f_bfree: 0,
283 f_bavail: 0,
284 f_files: 0,
285 f_ffree: 0,
286 f_fsid: [0; 2],
287 f_namelen: 0,
288 f_frsize: 0,
289 f_flags: 0,
290 f_spare: [0; 4],
291 }
292 }
293}
294
295/// Linux stat 结构 (RISC-V 64位)
296///
297/// 必须与Linux内核的stat64结构完全匹配
298#[repr(C)]
299#[derive(Debug, Clone, Copy)]
300pub struct Stat {
301 /// 设备ID
302 pub st_dev: u64,
303
304 /// Inode号
305 pub st_ino: u64,
306
307 /// 文件类型和权限
308 pub st_mode: u32,
309
310 /// 硬链接数
311 pub st_nlink: u32,
312
313 /// 用户ID
314 pub st_uid: u32,
315
316 /// 组ID
317 pub st_gid: u32,
318
319 /// 设备ID (如果是特殊文件)
320 pub st_rdev: u64,
321
322 pub __pad1: u64,
323
324 /// 文件大小(字节)
325 pub st_size: i64,
326
327 /// 块大小
328 pub st_blksize: i32,
329
330 pub __pad2: i32,
331
332 /// 占用的块数 (512B)
333 pub st_blocks: i64,
334
335 /// 访问时间(秒)
336 pub st_atime_sec: i64,
337
338 /// 访问时间(纳秒)
339 pub st_atime_nsec: i64,
340
341 /// 修改时间(秒)
342 pub st_mtime_sec: i64,
343
344 /// 修改时间(纳秒)
345 pub st_mtime_nsec: i64,
346
347 /// 状态改变时间(秒)
348 pub st_ctime_sec: i64,
349
350 /// 状态改变时间(纳秒)
351 pub st_ctime_nsec: i64,
352
353 pub __unused: [i32; 2],
354}
355
356/// Linux dirent64 结构
357///
358/// 用于getdents64系统调用
359#[repr(C)]
360pub struct LinuxDirent64 {
361 /// Inode号
362 pub d_ino: u64,
363
364 /// 到下一个dirent的偏移
365 pub d_off: i64,
366
367 /// 这个dirent的长度
368 pub d_reclen: u16,
369
370 /// 文件类型
371 pub d_type: u8,
372 // d_name: [u8] // 文件名(变长,以\0结尾)
373}
374
375impl LinuxDirent64 {
376 const BASE_SIZE: usize = 19;
377
378 /// 计算包含文件名的总长度(8字节对齐)
379 pub fn total_len(name: &str) -> usize {
380 let len = Self::BASE_SIZE + name.len() + 1; // +1 for null terminator
381 (len + 7) & !7 // 8字节对齐
382 }
383}