Skip to main content

strat9_kernel/syscall/
fcntl.rs

1//! `fcntl()` syscall implementation for file descriptor control.
2//!
3//! Provides fcntl operations for file descriptor flags (CLOEXEC).
4
5use crate::{process::current_task_clone, syscall::error::SyscallError, vfs::scheme::OpenFlags};
6
7// fcntl commands
8pub const F_DUPFD: u64 = 0;
9pub const F_GETFD: u64 = 1;
10pub const F_SETFD: u64 = 2;
11pub const F_GETFL: u64 = 3;
12pub const F_SETFL: u64 = 4;
13
14// File descriptor flags
15pub const FD_CLOEXEC: u64 = 1;
16
17/// SYS_FCNTL (313): manipulate file descriptor.
18///
19/// Supported commands:
20/// - F_DUPFD (0): Duplicate file descriptor
21/// - F_GETFD (1): Get file descriptor flags
22/// - F_SETFD (2): Set file descriptor flags (FD_CLOEXEC)
23/// - F_GETFL (3): Get file status flags
24/// - F_SETFL (4): Set file status flags (O_APPEND, O_NONBLOCK)
25pub fn sys_fcntl(fd: u64, cmd: u64, arg: u64) -> Result<u64, SyscallError> {
26    if fd > u32::MAX as u64 {
27        return Err(SyscallError::BadHandle);
28    }
29    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
30
31    match cmd {
32        F_GETFD => unsafe {
33            let fd_table = &*task.process.fd_table.get();
34            let cloexec = fd_table.get_cloexec(fd as u32)?;
35            Ok(if cloexec { FD_CLOEXEC } else { 0 })
36        },
37        F_SETFD => unsafe {
38            let fd_table = &mut *task.process.fd_table.get();
39            let cloexec = (arg & FD_CLOEXEC) != 0;
40            fd_table.set_cloexec(fd as u32, cloexec)?;
41            Ok(0)
42        },
43        F_DUPFD => {
44            if arg > u32::MAX as u64 {
45                return Err(SyscallError::InvalidArgument);
46            }
47            unsafe {
48                let fd_table = &mut *task.process.fd_table.get();
49                let new_fd = fd_table.duplicate_from(fd as u32, arg as u32)?;
50                Ok(new_fd as u64)
51            }
52        }
53        F_GETFL => {
54            let fd_table = unsafe { &*task.process.fd_table.get() };
55            let file = fd_table.get(fd as u32)?;
56            Ok(file.open_flags().bits() as u64)
57        }
58        F_SETFL => {
59            // Only O_APPEND, O_NONBLOCK, O_ASYNC can be changed via F_SETFL
60            const CHANGEABLE: u32 = OpenFlags::APPEND.bits() | OpenFlags::NONBLOCK.bits();
61            let new_flags = OpenFlags::from_bits_truncate((arg as u32) & CHANGEABLE);
62            unsafe {
63                let fd_table = &mut *task.process.fd_table.get();
64                fd_table.replace_open_flags(fd as u32, new_flags)?;
65            }
66            Ok(0)
67        }
68        _ => Err(SyscallError::InvalidArgument),
69    }
70}