starry_api/imp/fs/
fd_ops.rs

1use core::{
2    ffi::{c_char, c_int},
3    panic,
4};
5
6use alloc::string::ToString;
7use axerrno::{AxError, LinuxError, LinuxResult};
8use axfs::fops::OpenOptions;
9use linux_raw_sys::general::{
10    __kernel_mode_t, AT_FDCWD, F_DUPFD, F_DUPFD_CLOEXEC, F_SETFL, O_APPEND, O_CREAT, O_DIRECTORY,
11    O_NONBLOCK, O_PATH, O_RDONLY, O_TRUNC, O_WRONLY,
12};
13
14use crate::{
15    file::{Directory, FD_TABLE, File, FileLike, add_file_like, close_file_like, get_file_like},
16    path::handle_file_path,
17    ptr::UserConstPtr,
18};
19
20const O_EXEC: u32 = O_PATH;
21
22/// Convert open flags to [`OpenOptions`].
23fn flags_to_options(flags: c_int, _mode: __kernel_mode_t) -> OpenOptions {
24    let flags = flags as u32;
25    let mut options = OpenOptions::new();
26    match flags & 0b11 {
27        O_RDONLY => options.read(true),
28        O_WRONLY => options.write(true),
29        _ => {
30            options.read(true);
31            options.write(true);
32        }
33    };
34    if flags & O_APPEND != 0 {
35        options.append(true);
36    }
37    if flags & O_TRUNC != 0 {
38        options.truncate(true);
39    }
40    if flags & O_CREAT != 0 {
41        options.create(true);
42    }
43    if flags & O_EXEC != 0 {
44        //options.create_new(true);
45        options.execute(true);
46    }
47    if flags & O_DIRECTORY != 0 {
48        options.directory(true);
49    }
50    options
51}
52
53/// Open or create a file.
54/// fd: file descriptor
55/// filename: file path to be opened or created
56/// flags: open flags
57/// mode: see man 7 inode
58/// return new file descriptor if succeed, or return -1.
59pub fn sys_openat(
60    dirfd: c_int,
61    path: UserConstPtr<c_char>,
62    flags: i32,
63    mode: __kernel_mode_t,
64) -> LinuxResult<isize> {
65    let path = path.get_as_str()?;
66    let opts = flags_to_options(flags, mode);
67    debug!("sys_openat <= {} {} {:?}", dirfd, path, opts);
68
69    let dir = if path.starts_with('/') || dirfd == AT_FDCWD {
70        None
71    } else {
72        Some(Directory::from_fd(dirfd)?)
73    };
74    let real_path = handle_file_path(dirfd, path)?;
75
76    if !opts.has_directory() {
77        match dir.as_ref().map_or_else(
78            || axfs::fops::File::open(path, &opts),
79            |dir| dir.inner().open_file_at(path, &opts),
80        ) {
81            Err(AxError::IsADirectory) => {}
82            r => {
83                let fd = File::new(r?, real_path.to_string()).add_to_fd_table()?;
84                return Ok(fd as _);
85            }
86        }
87    }
88
89    let fd = Directory::new(
90        dir.map_or_else(
91            || axfs::fops::Directory::open_dir(path, &opts),
92            |dir| dir.inner().open_dir_at(path, &opts),
93        )?,
94        real_path.to_string(),
95    )
96    .add_to_fd_table()?;
97    Ok(fd as _)
98}
99
100/// Open a file by `filename` and insert it into the file descriptor table.
101///
102/// Return its index in the file table (`fd`). Return `EMFILE` if it already
103/// has the maximum number of files open.
104pub fn sys_open(
105    path: UserConstPtr<c_char>,
106    flags: i32,
107    mode: __kernel_mode_t,
108) -> LinuxResult<isize> {
109    sys_openat(AT_FDCWD as _, path, flags, mode)
110}
111
112pub fn sys_close(fd: c_int) -> LinuxResult<isize> {
113    debug!("sys_close <= {}", fd);
114    close_file_like(fd)?;
115    Ok(0)
116}
117
118fn dup_fd(old_fd: c_int) -> LinuxResult<isize> {
119    let f = get_file_like(old_fd)?;
120    let new_fd = add_file_like(f)?;
121    Ok(new_fd as _)
122}
123
124pub fn sys_dup(old_fd: c_int) -> LinuxResult<isize> {
125    debug!("sys_dup <= {}", old_fd);
126    dup_fd(old_fd)
127}
128
129pub fn sys_dup2(old_fd: c_int, new_fd: c_int) -> LinuxResult<isize> {
130    debug!("sys_dup2 <= old_fd: {}, new_fd: {}", old_fd, new_fd);
131    let mut fd_table = FD_TABLE.write();
132    let f = fd_table
133        .get(old_fd as _)
134        .cloned()
135        .ok_or(LinuxError::EBADF)?;
136
137    if old_fd != new_fd {
138        fd_table.remove(new_fd as _);
139        fd_table
140            .add_at(new_fd as _, f)
141            .unwrap_or_else(|_| panic!("new_fd should be valid"));
142    }
143
144    Ok(new_fd as _)
145}
146
147pub fn sys_fcntl(fd: c_int, cmd: c_int, arg: usize) -> LinuxResult<isize> {
148    debug!("sys_fcntl <= fd: {} cmd: {} arg: {}", fd, cmd, arg);
149
150    match cmd as u32 {
151        F_DUPFD => dup_fd(fd),
152        F_DUPFD_CLOEXEC => {
153            warn!("sys_fcntl: treat F_DUPFD_CLOEXEC as F_DUPFD");
154            dup_fd(fd)
155        }
156        F_SETFL => {
157            if fd == 0 || fd == 1 || fd == 2 {
158                return Ok(0);
159            }
160            get_file_like(fd)?.set_nonblocking(arg & (O_NONBLOCK as usize) > 0)?;
161            Ok(0)
162        }
163        _ => {
164            warn!("unsupported fcntl parameters: cmd: {}", cmd);
165            Ok(0)
166        }
167    }
168}