starry_api/imp/fs/
fd_ops.rs1use 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
22fn 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.execute(true);
46 }
47 if flags & O_DIRECTORY != 0 {
48 options.directory(true);
49 }
50 options
51}
52
53pub 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
100pub 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}