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}