os/kernel/syscall/
fcntl.rs

1//! fcntl 系统调用实现
2
3use crate::arch::trap::SumGuard;
4use crate::kernel::current_cpu;
5use crate::uapi::errno::EINVAL;
6use crate::uapi::fcntl::{FcntlCmd, FdFlags, FileStatusFlags, Flock, LockType};
7use crate::vfs::{FsError, OpenFlags, file_lock_manager};
8use alloc::sync::Arc;
9
10/// fcntl - 文件描述符操作
11///
12/// # 参数
13/// - `fd`: 文件描述符
14/// - `cmd`: fcntl 命令
15/// - `arg`: 命令参数(根据 cmd 的不同而不同)
16///
17/// # 返回值
18/// 成功返回非负值,失败返回负 errno
19pub fn fcntl(fd: usize, cmd_raw: i32, arg: usize) -> isize {
20    // 解析命令
21    let cmd = match FcntlCmd::from_raw(cmd_raw) {
22        Some(c) => c,
23        None => return -(EINVAL as isize),
24    };
25
26    let task = current_cpu().lock().current_task.as_ref().unwrap().clone();
27
28    match cmd {
29        FcntlCmd::GetFd => {
30            // F_GETFD: 获取文件描述符标志
31            match task.lock().fd_table.get_fd_flags(fd) {
32                Ok(flags) => flags.bits() as isize,
33                Err(e) => e.to_errno(),
34            }
35        }
36
37        FcntlCmd::SetFd => {
38            // F_SETFD: 设置文件描述符标志
39            let flags = match FdFlags::from_bits(arg as u32) {
40                Some(f) => f,
41                None => return -(EINVAL as isize),
42            };
43            match task.lock().fd_table.set_fd_flags(fd, flags) {
44                Ok(()) => 0,
45                Err(e) => e.to_errno(),
46            }
47        }
48
49        FcntlCmd::GetFl => {
50            // F_GETFL: 获取文件状态标志
51            let file = match task.lock().fd_table.get(fd) {
52                Ok(f) => f,
53                Err(e) => return e.to_errno(),
54            };
55            file.flags().bits() as isize
56        }
57
58        FcntlCmd::SetFl => {
59            // F_SETFL: 设置文件状态标志
60            // 只能修改特定标志(APPEND, NONBLOCK, ASYNC, DIRECT, NOATIME)
61            let new_flags_raw = arg as u32;
62            let new_status_flags = match FileStatusFlags::from_bits(new_flags_raw) {
63                Some(f) => f,
64                None => return -(EINVAL as isize),
65            };
66
67            // 检查是否只修改允许修改的标志
68            if !new_status_flags.is_modifiable() {
69                return -(EINVAL as isize);
70            }
71
72            let file = match task.lock().fd_table.get(fd) {
73                Ok(f) => f,
74                Err(e) => return e.to_errno(),
75            };
76
77            // 获取当前标志
78            let current_flags = file.flags();
79
80            // 保留访问模式和其他不可修改的标志
81            let access_mode = current_flags & OpenFlags::O_ACCMODE;
82            let non_modifiable = current_flags
83                & !(OpenFlags::O_APPEND
84                    | OpenFlags::O_NONBLOCK
85                    | OpenFlags::from_bits_truncate(FileStatusFlags::ASYNC.bits())
86                    | OpenFlags::from_bits_truncate(FileStatusFlags::DIRECT.bits())
87                    | OpenFlags::from_bits_truncate(FileStatusFlags::NOATIME.bits()));
88
89            // 构建新的标志:保留访问模式 + 保留不可修改部分 + 新的可修改标志
90            let final_flags = access_mode
91                | non_modifiable
92                | OpenFlags::from_bits_truncate(new_status_flags.bits());
93
94            // 调用 File trait 的 set_status_flags 方法
95            match file.set_status_flags(final_flags) {
96                Ok(()) => 0,
97                Err(e) => e.to_errno(),
98            }
99        }
100
101        // 文件描述符复制
102        FcntlCmd::DupFd => {
103            // F_DUPFD: 复制文件描述符,新 fd >= arg
104            fcntl_dupfd(&task, fd, arg, false)
105        }
106
107        FcntlCmd::DupFdCloexec => {
108            // F_DUPFD_CLOEXEC: 复制文件描述符并设置 CLOEXEC
109            fcntl_dupfd(&task, fd, arg, true)
110        }
111
112        // === 文件锁操作 ===
113        FcntlCmd::GetLk => {
114            // F_GETLK: 测试锁
115            let flock_ptr = arg as *mut Flock;
116            if flock_ptr.is_null() {
117                return -(EINVAL as isize);
118            }
119
120            // 读取用户空间的 flock 结构
121            let mut flock = {
122                let _guard = SumGuard::new();
123                unsafe { core::ptr::read(flock_ptr) }
124            };
125
126            // 获取文件对象
127            let file = match task.lock().fd_table.get(fd) {
128                Ok(f) => f,
129                Err(e) => return e.to_errno(),
130            };
131
132            // 获取 inode 元数据(需要设备号和 inode 号)
133            let inode = match file.inode() {
134                Ok(i) => i,
135                Err(_) => {
136                    // 不支持锁的文件类型(如管道)
137                    return FsError::InvalidArgument.to_errno();
138                }
139            };
140
141            let metadata = match inode.metadata() {
142                Ok(m) => m,
143                Err(e) => return e.to_errno(),
144            };
145
146            // 获取当前进程 PID
147            let pid = task.lock().pid as i32;
148
149            // 将相对偏移转换为绝对偏移
150            let file_offset = file.offset();
151            let file_size = metadata.size;
152            let (start, len) = match flock.to_absolute_range(file_offset, file_size) {
153                Ok(range) => range,
154                Err(_) => return -(EINVAL as isize),
155            };
156
157            // 测试锁
158            // TODO: 获取真实设备号
159            // 当前使用设备号 0,在单一文件系统场景下 inode 号足够区分文件。
160            // 未来改进:
161            // 1. 在 FileSystem trait 中添加 dev_id() 方法返回设备号
162            // 2. 在挂载时为每个文件系统分配唯一的设备号
163            // 3. 通过 dentry -> mount_point -> fs 获取设备号
164            let dev = 0;
165            let ino = metadata.inode_no as u64;
166            if let Err(e) = file_lock_manager().test_lock(dev, ino, start, len, &mut flock, pid) {
167                return e.to_errno();
168            }
169
170            // 将结果写回用户空间
171            {
172                let _guard = SumGuard::new();
173                unsafe { core::ptr::write(flock_ptr, flock) };
174            }
175
176            0
177        }
178
179        FcntlCmd::SetLk | FcntlCmd::SetLkW => {
180            // F_SETLK / F_SETLKW: 设置或释放锁
181            let blocking = matches!(cmd, FcntlCmd::SetLkW);
182            let flock_ptr = arg as *const Flock;
183            if flock_ptr.is_null() {
184                return -(EINVAL as isize);
185            }
186
187            // 读取用户空间的 flock 结构
188            let flock = {
189                let _guard = SumGuard::new();
190                unsafe { core::ptr::read(flock_ptr) }
191            };
192
193            // 解析锁类型
194            let lock_type = match LockType::from_raw(flock.l_type) {
195                Some(t) => t,
196                None => return -(EINVAL as isize),
197            };
198
199            // 获取文件对象
200            let file = match task.lock().fd_table.get(fd) {
201                Ok(f) => f,
202                Err(e) => return e.to_errno(),
203            };
204
205            // 获取 inode
206            let inode = match file.inode() {
207                Ok(i) => i,
208                Err(_) => {
209                    // 不支持锁的文件类型
210                    return FsError::InvalidArgument.to_errno();
211                }
212            };
213
214            let metadata = match inode.metadata() {
215                Ok(m) => m,
216                Err(e) => return e.to_errno(),
217            };
218
219            // 转换为绝对偏移
220            let file_offset = file.offset();
221            let file_size = metadata.size;
222            let (start, len) = match flock.to_absolute_range(file_offset, file_size) {
223                Ok(range) => range,
224                Err(_) => return -(EINVAL as isize),
225            };
226
227            // 获取当前进程 PID
228            let pid = task.lock().pid as i32;
229
230            // 设置锁
231            // TODO: 获取真实设备号(与上面 F_GETLK 的 TODO 相同)
232            let dev = 0;
233            let ino = metadata.inode_no as u64;
234            match file_lock_manager().set_lock(dev, ino, start, len, lock_type, pid, blocking) {
235                Ok(()) => 0,
236                Err(e) => e.to_errno(),
237            }
238        }
239
240        //  异步 I/O 和信号
241        FcntlCmd::GetOwn => {
242            // F_GETOWN: 获取异步 I/O 所有者
243            let file = match task.lock().fd_table.get(fd) {
244                Ok(f) => f,
245                Err(e) => return e.to_errno(),
246            };
247
248            match file.get_owner() {
249                Ok(pid) => pid as isize,
250                Err(e) => e.to_errno(),
251            }
252        }
253
254        FcntlCmd::SetOwn => {
255            // F_SETOWN: 设置异步 I/O 所有者
256            let pid = arg as i32;
257            let file = match task.lock().fd_table.get(fd) {
258                Ok(f) => f,
259                Err(e) => return e.to_errno(),
260            };
261
262            match file.set_owner(pid) {
263                Ok(()) => 0,
264                Err(e) => e.to_errno(),
265            }
266        }
267
268        FcntlCmd::SetSig | FcntlCmd::GetSig => {
269            // F_SETSIG / F_GETSIG: 设置/获取异步 I/O 信号
270            // 默认是 SIGIO,暂不支持自定义信号
271            FsError::NotSupported.to_errno()
272        }
273
274        //  管道大小
275        FcntlCmd::GetPipeSz => {
276            // F_GETPIPE_SZ: 获取管道大小
277            let file = match task.lock().fd_table.get(fd) {
278                Ok(f) => f,
279                Err(e) => return e.to_errno(),
280            };
281
282            match file.get_pipe_size() {
283                Ok(size) => size as isize,
284                Err(e) => e.to_errno(),
285            }
286        }
287
288        FcntlCmd::SetPipeSz => {
289            // F_SETPIPE_SZ: 设置管道大小
290            let new_size = arg;
291            let file = match task.lock().fd_table.get(fd) {
292                Ok(f) => f,
293                Err(e) => return e.to_errno(),
294            };
295
296            match file.set_pipe_size(new_size) {
297                Ok(()) => new_size as isize,
298                Err(e) => e.to_errno(),
299            }
300        }
301    }
302}
303
304/// F_DUPFD / F_DUPFD_CLOEXEC 的辅助函数
305fn fcntl_dupfd(
306    task: &Arc<crate::sync::SpinLock<crate::kernel::task::TaskStruct>>,
307    old_fd: usize,
308    min_fd: usize,
309    cloexec: bool,
310) -> isize {
311    let flags = if cloexec {
312        FdFlags::CLOEXEC
313    } else {
314        FdFlags::empty()
315    };
316
317    // 使用 FDTable 的标准方法进行复制
318    let new_fd = match task.lock().fd_table.dup_from(old_fd, min_fd, flags) {
319        Ok(fd) => {
320            crate::earlyprintln!(
321                "fcntl_dupfd: old_fd={} -> new_fd={}, min_fd={}, cloexec={}",
322                old_fd,
323                fd,
324                min_fd,
325                cloexec
326            );
327            fd
328        }
329        Err(e) => {
330            crate::earlyprintln!("fcntl_dupfd: failed: {:?}", e);
331            return e.to_errno();
332        }
333    };
334
335    new_fd as isize
336}