1use 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
10pub fn fcntl(fd: usize, cmd_raw: i32, arg: usize) -> isize {
20 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 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 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 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 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 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 let current_flags = file.flags();
79
80 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 let final_flags = access_mode
91 | non_modifiable
92 | OpenFlags::from_bits_truncate(new_status_flags.bits());
93
94 match file.set_status_flags(final_flags) {
96 Ok(()) => 0,
97 Err(e) => e.to_errno(),
98 }
99 }
100
101 FcntlCmd::DupFd => {
103 fcntl_dupfd(&task, fd, arg, false)
105 }
106
107 FcntlCmd::DupFdCloexec => {
108 fcntl_dupfd(&task, fd, arg, true)
110 }
111
112 FcntlCmd::GetLk => {
114 let flock_ptr = arg as *mut Flock;
116 if flock_ptr.is_null() {
117 return -(EINVAL as isize);
118 }
119
120 let mut flock = {
122 let _guard = SumGuard::new();
123 unsafe { core::ptr::read(flock_ptr) }
124 };
125
126 let file = match task.lock().fd_table.get(fd) {
128 Ok(f) => f,
129 Err(e) => return e.to_errno(),
130 };
131
132 let inode = match file.inode() {
134 Ok(i) => i,
135 Err(_) => {
136 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 let pid = task.lock().pid as i32;
148
149 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 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 {
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 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 let flock = {
189 let _guard = SumGuard::new();
190 unsafe { core::ptr::read(flock_ptr) }
191 };
192
193 let lock_type = match LockType::from_raw(flock.l_type) {
195 Some(t) => t,
196 None => return -(EINVAL as isize),
197 };
198
199 let file = match task.lock().fd_table.get(fd) {
201 Ok(f) => f,
202 Err(e) => return e.to_errno(),
203 };
204
205 let inode = match file.inode() {
207 Ok(i) => i,
208 Err(_) => {
209 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 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 let pid = task.lock().pid as i32;
229
230 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 FcntlCmd::GetOwn => {
242 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 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 FsError::NotSupported.to_errno()
272 }
273
274 FcntlCmd::GetPipeSz => {
276 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 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
304fn 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 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}