arceos_posix_api/imp/
fd_ops.rs

1use alloc::sync::Arc;
2use core::ffi::c_int;
3
4use axerrno::{LinuxError, LinuxResult};
5use axio::PollState;
6use axns::{ResArc, def_resource};
7use flatten_objects::FlattenObjects;
8use spin::RwLock;
9
10use crate::ctypes;
11use crate::imp::stdio::{stdin, stdout};
12
13pub const AX_FILE_LIMIT: usize = 1024;
14
15#[allow(dead_code)]
16pub trait FileLike: Send + Sync {
17    fn read(&self, buf: &mut [u8]) -> LinuxResult<usize>;
18    fn write(&self, buf: &[u8]) -> LinuxResult<usize>;
19    fn stat(&self) -> LinuxResult<ctypes::stat>;
20    fn into_any(self: Arc<Self>) -> Arc<dyn core::any::Any + Send + Sync>;
21    fn poll(&self) -> LinuxResult<PollState>;
22    fn set_nonblocking(&self, nonblocking: bool) -> LinuxResult;
23}
24
25def_resource! {
26    pub static FD_TABLE: ResArc<RwLock<FlattenObjects<Arc<dyn FileLike>, AX_FILE_LIMIT>>> = ResArc::new();
27}
28
29impl FD_TABLE {
30    /// Return a copy of the inner table.
31    pub fn copy_inner(&self) -> RwLock<FlattenObjects<Arc<dyn FileLike>, AX_FILE_LIMIT>> {
32        let table = self.read();
33        let mut new_table = FlattenObjects::new();
34        for id in table.ids() {
35            let _ = new_table.add_at(id, table.get(id).unwrap().clone());
36        }
37        RwLock::new(new_table)
38    }
39}
40
41/// Get a file by `fd`.
42pub fn get_file_like(fd: c_int) -> LinuxResult<Arc<dyn FileLike>> {
43    FD_TABLE
44        .read()
45        .get(fd as usize)
46        .cloned()
47        .ok_or(LinuxError::EBADF)
48}
49
50/// Add a file to the file descriptor table.
51pub fn add_file_like(f: Arc<dyn FileLike>) -> LinuxResult<c_int> {
52    Ok(FD_TABLE.write().add(f).map_err(|_| LinuxError::EMFILE)? as c_int)
53}
54
55/// Close a file by `fd`.
56pub fn close_file_like(fd: c_int) -> LinuxResult {
57    let f = FD_TABLE
58        .write()
59        .remove(fd as usize)
60        .ok_or(LinuxError::EBADF)?;
61    drop(f);
62    Ok(())
63}
64
65/// Close a file by `fd`.
66pub fn sys_close(fd: c_int) -> c_int {
67    debug!("sys_close <= {}", fd);
68    if (0..=2).contains(&fd) {
69        return 0; // stdin, stdout, stderr
70    }
71    syscall_body!(sys_close, close_file_like(fd).map(|_| 0))
72}
73
74fn dup_fd(old_fd: c_int) -> LinuxResult<c_int> {
75    let f = get_file_like(old_fd)?;
76    let new_fd = add_file_like(f)?;
77    Ok(new_fd)
78}
79
80/// Duplicate a file descriptor.
81pub fn sys_dup(old_fd: c_int) -> c_int {
82    debug!("sys_dup <= {}", old_fd);
83    syscall_body!(sys_dup, dup_fd(old_fd))
84}
85
86/// Duplicate a file descriptor, but it uses the file descriptor number specified in `new_fd`.
87///
88/// TODO: `dup2` should forcibly close new_fd if it is already opened.
89pub fn sys_dup2(old_fd: c_int, new_fd: c_int) -> c_int {
90    debug!("sys_dup2 <= old_fd: {}, new_fd: {}", old_fd, new_fd);
91    syscall_body!(sys_dup2, {
92        if old_fd == new_fd {
93            let r = sys_fcntl(old_fd, ctypes::F_GETFD as _, 0);
94            if r >= 0 {
95                return Ok(old_fd);
96            } else {
97                return Ok(r);
98            }
99        }
100        if new_fd as usize >= AX_FILE_LIMIT {
101            return Err(LinuxError::EBADF);
102        }
103
104        let f = get_file_like(old_fd)?;
105        FD_TABLE
106            .write()
107            .add_at(new_fd as usize, f)
108            .map_err(|_| LinuxError::EMFILE)?;
109
110        Ok(new_fd)
111    })
112}
113
114/// Manipulate file descriptor.
115///
116/// TODO: `SET/GET` command is ignored, hard-code stdin/stdout
117pub fn sys_fcntl(fd: c_int, cmd: c_int, arg: usize) -> c_int {
118    debug!("sys_fcntl <= fd: {} cmd: {} arg: {}", fd, cmd, arg);
119    syscall_body!(sys_fcntl, {
120        match cmd as u32 {
121            ctypes::F_DUPFD => dup_fd(fd),
122            ctypes::F_DUPFD_CLOEXEC => {
123                // TODO: Change fd flags
124                dup_fd(fd)
125            }
126            ctypes::F_SETFL => {
127                if fd == 0 || fd == 1 || fd == 2 {
128                    return Ok(0);
129                }
130                get_file_like(fd)?.set_nonblocking(arg & (ctypes::O_NONBLOCK as usize) > 0)?;
131                Ok(0)
132            }
133            _ => {
134                warn!("unsupported fcntl parameters: cmd {}", cmd);
135                Ok(0)
136            }
137        }
138    })
139}
140
141#[ctor_bare::register_ctor]
142fn init_stdio() {
143    let mut fd_table = flatten_objects::FlattenObjects::new();
144    fd_table
145        .add_at(0, Arc::new(stdin()) as _)
146        .unwrap_or_else(|_| panic!()); // stdin
147    fd_table
148        .add_at(1, Arc::new(stdout()) as _)
149        .unwrap_or_else(|_| panic!()); // stdout
150    fd_table
151        .add_at(2, Arc::new(stdout()) as _)
152        .unwrap_or_else(|_| panic!()); // stderr
153    FD_TABLE.init_new(spin::RwLock::new(fd_table));
154}