os/kernel/syscall/
util.rs

1//! 系统调用辅助函数
2
3use core::ffi::{CStr, c_char};
4
5use alloc::{
6    format,
7    string::{String, ToString},
8    sync::Arc,
9    vec::Vec,
10};
11
12use crate::{
13    kernel::current_task,
14    uapi::{errno::EINVAL, log::SyslogAction},
15    vfs::{
16        DENTRY_CACHE, Dentry, File, FileMode, FsError, InodeType, OpenFlags, get_root_dentry,
17        impls::{BlockDeviceFile, CharDeviceFile, RegFile},
18        split_path, vfs_lookup_from,
19    },
20};
21
22/// 从用户空间获取路径字符串
23/// # 参数
24/// - `path`: 指向用户空间路径字符串的指针
25/// # 返回值
26/// - 成功时返回路径字符串的引用
27/// - 失败时返回错误字符串
28pub fn get_path_safe(path: *const c_char) -> Result<&'static str, &'static str> {
29    // 必须在 unsafe 块中进行,因为依赖 C 的正确性
30    let c_str = unsafe {
31        // 检查指针是否为 NULL (空指针)
32        if path.is_null() {
33            return Err("Path pointer is NULL");
34        }
35        // 转换为安全的 &CStr 引用。如果指针无效或非空终止,这里会发生未定义行为 (UB)
36        CStr::from_ptr(path)
37    };
38
39    // 转换为 Rust 的 &str。to_str() 会检查 UTF-8 有效性
40    match c_str.to_str() {
41        Ok(s) => Ok(s),
42        Err(_) => Err("Path is not valid UTF-8"),
43    }
44}
45
46/// 从用户空间获取参数字符串数组
47///# 参数
48/// - `ptr_array`: 指向用户空间字符串指针数组的指针
49/// - `name`: 参数名称,用于错误报告
50/// # 返回值
51/// - 成功时返回包含参数字符串的 `Vec<String>`
52/// - 失败时返回错误字符串
53pub fn get_args_safe(
54    ptr_array: *const *const c_char,
55    name: &str, // 用于错误报告
56) -> Result<Vec<String>, String> {
57    let mut args = Vec::new();
58
59    // 1. 检查指针数组是否为 NULL
60    if ptr_array.is_null() {
61        return Ok(Vec::new()); // 可能是合法的空列表
62    }
63
64    // 必须在 unsafe 块中进行,因为涉及到裸指针操作
65    unsafe {
66        let mut current_ptr = ptr_array;
67
68        // 2. 迭代直到遇到 NULL 指针
69        while !(*current_ptr).is_null() {
70            let c_str = {
71                // 3. 将当前的 *const c_char 转换为 &CStr
72                CStr::from_ptr(*current_ptr)
73            };
74
75            // 4. 转换为 Rust String 并收集
76            match c_str.to_str() {
77                Ok(s) => args.push(s.to_string()),
78                Err(_) => {
79                    return Err(format!("{} contains non-UTF-8 string", name));
80                }
81            }
82
83            // 移动到数组的下一个元素
84            current_ptr = current_ptr.add(1);
85        }
86    }
87
88    Ok(args)
89}
90
91/// 解析at系列系统调用的路径
92///
93/// 这是系统调用层的辅助函数,处理 AT_FDCWD 和相对路径逻辑
94pub fn resolve_at_path(dirfd: i32, path: &str) -> Result<Option<Arc<Dentry>>, FsError> {
95    let base_dentry = if path.starts_with('/') {
96        get_root_dentry()?
97    } else if dirfd == super::fs::AT_FDCWD {
98        current_task()
99            .lock()
100            .fs
101            .lock()
102            .cwd
103            .clone()
104            .ok_or(FsError::NotSupported)?
105    } else {
106        // 对于文件描述符,我们需要获取对应的 dentry
107        let task = current_task();
108        let file = task.lock().fd_table.get(dirfd as usize)?;
109
110        // 验证是目录
111        let meta = file.metadata()?;
112        if meta.inode_type != InodeType::Directory {
113            return Err(FsError::NotDirectory);
114        }
115
116        if let Ok(dentry) = file.dentry() {
117            dentry
118        } else {
119            return Err(FsError::NotDirectory);
120        }
121    };
122
123    match vfs_lookup_from(base_dentry, path) {
124        Ok(d) => Ok(Some(d)),
125        Err(FsError::NotFound) => Ok(None),
126        Err(e) => Err(e),
127    }
128}
129
130/// 在指定目录下创建一个新文件
131///// # 参数
132/// - `dirfd`: 目录文件描述符,或 AT_FDCWD
133/// - `path`: 要创建的文件路径(相对于 dirfd)
134/// - `mode`: 文件权限模式
135/// 返回新创建的文件的 Dentry
136pub fn create_file_at(dirfd: i32, path: &str, mode: u32) -> Result<Arc<Dentry>, FsError> {
137    let (dir_path, filename) = split_path(path)?;
138    let parent_dentry = match resolve_at_path(dirfd, &dir_path)? {
139        Some(d) => d,
140        None => return Err(FsError::NotFound),
141    };
142
143    let meta = parent_dentry.inode.metadata()?;
144    if meta.inode_type != InodeType::Directory {
145        return Err(FsError::NotDirectory);
146    }
147
148    let file_mode = FileMode::from_bits_truncate(mode) | FileMode::S_IFREG;
149    let child_inode = parent_dentry.inode.create(&filename, file_mode)?;
150
151    let child_dentry = Dentry::new(filename.clone(), child_inode);
152    parent_dentry.add_child(child_dentry.clone());
153    DENTRY_CACHE.insert(&child_dentry);
154
155    Ok(child_dentry)
156}
157
158/// 验证 syslog 系统调用参数
159///
160/// 根据操作类型检查参数的有效性。
161///
162/// # 验证规则
163/// - **Read/ReadAll/ReadClear**: bufp != NULL, len >= 0
164/// - **ConsoleLevel**: len 必须在 1-8 范围内
165/// - **其他操作**: 忽略 bufp 和 len
166///
167/// # 返回值
168/// * `Ok(())` - 参数有效
169/// * `Err(EINVAL)` - 参数无效
170pub fn validate_syslog_args(action: SyslogAction, bufp: *mut u8, len: i32) -> Result<(), i32> {
171    match action {
172        SyslogAction::Read | SyslogAction::ReadAll | SyslogAction::ReadClear => {
173            // 这些操作需要有效的缓冲区
174            if bufp.is_null() {
175                return Err(EINVAL);
176            }
177            if len < 0 {
178                return Err(EINVAL);
179            }
180            // len == 0 是合法的,只是不会读取任何数据
181        }
182
183        SyslogAction::ConsoleLevel => {
184            // Linux 要求 console_loglevel 在 1-8 范围内
185            // 参考:kernel/printk/printk.c
186            if len < 1 || len > 8 {
187                return Err(EINVAL);
188            }
189        }
190
191        _ => {
192            // 其他操作不需要验证 bufp 和 len
193        }
194    }
195
196    Ok(())
197}
198
199/// 检查 syslog 操作权限
200///
201/// # 权限规则(完全遵循 Linux)
202/// 1. **特殊情况:ReadAll 和 SizeBuffer**
203///    - 如果 `dmesg_restrict == 0`:允许所有用户访问
204///    - 如果 `dmesg_restrict != 0`:需要特权
205/// 2. **其他操作**:
206///    - 需要以下任一权限:
207///      - `euid == 0` (root 用户)
208///      - `CAP_SYSLOG` (推荐)
209///      - `CAP_SYS_ADMIN` (向后兼容)
210///
211/// # 返回值
212/// * `Ok(())` - 有权限
213/// * `Err(EPERM)` - 权限不足
214pub fn check_syslog_permission(action: SyslogAction) -> Result<(), i32> {
215    // TODO: 等待完成用户管理和能力模型后再实现完整的权限检查
216    //
217    // 完整实现应该包括:
218    // 1. 检查 dmesg_restrict sysctl (ReadAll 和 SizeBuffer 特殊处理)
219    // 2. 检查 euid == 0 (root 用户)
220    // 3. 检查 CAP_SYSLOG 能力
221    // 4. 检查 CAP_SYS_ADMIN 能力 (向后兼容)
222    //
223    // 临时实现:允许所有操作(开发阶段)
224
225    // 特殊处理:ReadAll 和 SizeBuffer 可能允许非特权访问
226    if matches!(action, SyslogAction::ReadAll | SyslogAction::SizeBuffer) {
227        // 检查 dmesg_restrict sysctl
228        let dmesg_restrict = get_dmesg_restrict();
229        if dmesg_restrict == 0 {
230            return Ok(()); // 允许非特权访问
231        }
232    }
233
234    // TODO: 完整的权限检查实现
235    // 临时方案:暂时允许所有操作
236    Ok(())
237
238    /* 完整实现参考(等待用户管理模块):
239
240    // 获取当前任务
241    let task = current_task();
242    let task_locked = task.lock();
243
244    // 检查是否为 root
245    if task_locked.euid == 0 {
246        return Ok(());
247    }
248
249    // 检查 CAP_SYSLOG (Linux 2.6.38+)
250    if task_locked.capabilities.has_effective(Capability::CAP_SYSLOG) {
251        return Ok(());
252    }
253
254    // 检查 CAP_SYS_ADMIN (向后兼容)
255    if task_locked.capabilities.has_effective(Capability::CAP_SYS_ADMIN) {
256        return Ok(());
257    }
258
259    // 权限不足
260    Err(EPERM)
261    */
262}
263
264/// 获取 dmesg_restrict sysctl 值
265///
266/// # 返回值
267/// * `0` - 允许所有用户读取内核日志
268/// * `1` - 只允许特权用户读取
269///
270/// # TODO
271/// 目前硬编码为 0,需要实现真实的 sysctl 支持。
272#[inline]
273fn get_dmesg_restrict() -> u32 {
274    // TODO: 从 sysctl 系统读取真实值
275    // 参考路径: /proc/sys/kernel/dmesg_restrict
276    0
277}
278
279/// 获取第一个可用的块设备驱动
280///
281/// 简化版实现:直接返回第一个块设备
282///
283/// # 返回值
284/// * `Ok(Arc<dyn BlockDriver>)` - 成功获取块设备
285/// * `Err(errno)` - 没有可用的块设备
286pub fn get_first_block_device() -> Result<Arc<dyn crate::device::block::BlockDriver>, i32> {
287    use crate::device::BLK_DRIVERS;
288    use crate::uapi::errno::ENODEV;
289
290    let drivers = BLK_DRIVERS.read();
291
292    if drivers.is_empty() {
293        return Err(-ENODEV);
294    }
295
296    Ok(drivers[0].clone())
297}
298
299/// 刷新所有块设备
300///
301/// # 返回值
302/// 如果所有设备成功刷新返回 Ok(()),否则返回第一个错误
303pub fn flush_all_block_devices() -> Result<(), isize> {
304    use crate::{device::BLK_DRIVERS, uapi::errno::EIO};
305
306    let drivers = BLK_DRIVERS.read();
307
308    if drivers.is_empty() {
309        // 没有块设备也算成功(无事可做)
310        return Ok(());
311    }
312
313    for driver in drivers.iter() {
314        if !driver.flush() {
315            // VirtIO flush 失败
316            return Err(-EIO as isize);
317        }
318    }
319
320    Ok(())
321}
322
323/// 从文件描述符获取对应的块设备并刷新
324///
325/// 通过 fd -> dentry -> 路径 -> 挂载点 -> 文件系统 -> 同步
326///
327/// # 参数
328/// - `fd`: 文件描述符
329///
330/// # 返回值
331/// - Ok(()): 刷新成功
332/// - Err(-EBADF): 无效的文件描述符
333/// - Err(-EINVAL): fd 不支持同步(如 pipe、socket)
334/// - Err(-EIO): 块设备刷新失败
335pub fn flush_block_device_by_fd(fd: usize) -> Result<(), isize> {
336    use crate::{uapi::errno::EIO, vfs::MOUNT_TABLE};
337
338    // 1. 获取文件对象
339    let task = current_task();
340    let file = task.lock().fd_table.get(fd).map_err(|e| e.to_errno())?;
341
342    // 2. 获取 dentry (如果不支持则说明是管道等特殊文件)
343    let dentry = file.dentry().map_err(|e| e.to_errno())?;
344
345    // 3. 获取文件的完整路径
346    let path = dentry.full_path();
347
348    // 4. 通过路径查找对应的挂载点
349    let mount_point = MOUNT_TABLE
350        .find_mount(&path)
351        .ok_or_else(|| FsError::NotSupported.to_errno())?;
352
353    // 5. 调用文件系统的 sync 方法
354    mount_point.fs.sync().map_err(|_| -EIO as isize)?;
355
356    Ok(())
357}
358
359/// 根据 dirfd 和路径解析 dentry,支持符号链接控制
360///
361/// 这是对 `resolve_at_path` 的扩展,支持控制是否跟随最后一个符号链接。
362///
363/// # 参数
364/// * `dirfd` - 目录文件描述符(AT_FDCWD 表示当前目录)
365/// * `path` - 文件路径
366/// * `follow_symlink` - 是否跟随最后一个符号链接
367///
368/// # 返回值
369/// * `Ok(Arc<Dentry>)` - 成功找到文件
370/// * `Err(FsError)` - 查找失败
371///
372/// # 示例
373/// ```rust
374/// // 跟随符号链接(默认行为)
375/// let dentry = resolve_at_path_with_flags(AT_FDCWD, "/path/to/file", true)?;
376///
377/// // 不跟随符号链接(用于 lstat, lchown 等)
378/// let dentry = resolve_at_path_with_flags(AT_FDCWD, "/path/to/symlink", false)?;
379/// ```
380pub fn resolve_at_path_with_flags(
381    dirfd: i32,
382    path: &str,
383    follow_symlink: bool,
384) -> Result<Arc<Dentry>, FsError> {
385    use crate::vfs::vfs_lookup_no_follow;
386
387    if follow_symlink {
388        // 跟随符号链接,使用标准的 resolve_at_path
389        resolve_at_path(dirfd, path)?.ok_or(FsError::NotFound)
390    } else {
391        // 不跟随符号链接
392        if path.starts_with('/') {
393            // 绝对路径
394            vfs_lookup_no_follow(path)
395        } else {
396            // 相对路径,需要从 dirfd 开始
397            let base_dentry = if dirfd == super::fs::AT_FDCWD {
398                // 使用当前工作目录
399                current_task()
400                    .lock()
401                    .fs
402                    .lock()
403                    .cwd
404                    .clone()
405                    .ok_or(FsError::NotFound)?
406            } else {
407                // 使用 dirfd 指向的目录
408                let task = current_task();
409                let file = task.lock().fd_table.get(dirfd as usize)?;
410                file.dentry()?
411            };
412
413            // 构建完整路径
414            let full_path = if base_dentry.full_path() == "/" {
415                format!("/{}", path)
416            } else {
417                format!("{}/{}", base_dentry.full_path(), path)
418            };
419
420            vfs_lookup_no_follow(&full_path)
421        }
422    }
423}
424
425/// 根据 inode 类型创建对应的 File 实例
426pub fn create_file_from_dentry(
427    dentry: Arc<Dentry>,
428    flags: OpenFlags,
429) -> Result<Arc<dyn File>, FsError> {
430    let inode_type = dentry.inode.metadata()?.inode_type;
431
432    let file: Arc<dyn File> = match inode_type {
433        InodeType::File | InodeType::Directory | InodeType::Symlink => {
434            // 普通文件、目录、符号链接
435            Arc::new(RegFile::new(dentry, flags))
436        }
437        InodeType::CharDevice => {
438            // 字符设备
439            Arc::new(CharDeviceFile::new(dentry, flags)?)
440        }
441        InodeType::BlockDevice => {
442            // 块设备
443            Arc::new(BlockDeviceFile::new(dentry, flags)?)
444        }
445        InodeType::Socket | InodeType::Fifo => {
446            // FIFO(命名管道) 和 Unix 域套接字 暂不支持
447            return Err(FsError::NotSupported);
448        }
449    };
450
451    Ok(file)
452}