1use starnix_sync::{Locked, Unlocked};
6
7use crate::mm::MemoryAccessorExt;
8use crate::signals::syscalls::sys_signalfd4;
9use crate::task::CurrentTask;
10use crate::task::syscalls::do_clone;
11use crate::time::utc;
12use crate::vfs::syscalls::{
13 poll, sys_dup3, sys_epoll_create1, sys_epoll_pwait, sys_eventfd2, sys_faccessat, sys_fchmodat,
14 sys_fchownat, sys_inotify_init1, sys_linkat, sys_mkdirat, sys_mknodat, sys_newfstatat,
15 sys_openat, sys_pipe2, sys_readlinkat, sys_renameat2, sys_symlinkat, sys_unlinkat,
16};
17use crate::vfs::{DirentSink32, FdNumber};
18use starnix_logging::track_stub;
19use starnix_types::time::{
20 duration_from_poll_timeout, duration_from_timeval, timeval_from_duration,
21};
22use starnix_uapi::device_id::DeviceId;
23use starnix_uapi::errors::Errno;
24use starnix_uapi::file_mode::FileMode;
25use starnix_uapi::open_flags::OpenFlags;
26use starnix_uapi::signals::{SIGCHLD, SigSet};
27use starnix_uapi::user_address::{UserAddress, UserCString, UserRef};
28use starnix_uapi::vfs::EpollEvent;
29use starnix_uapi::{
30 __kernel_time_t, ARCH_SET_FS, ARCH_SET_GS, AT_REMOVEDIR, AT_SYMLINK_NOFOLLOW, CLONE_VFORK,
31 CLONE_VM, CSIGNAL, ITIMER_REAL, clone_args, errno, error, gid_t, itimerval, pid_t, pollfd,
32 tid_t, uapi, uid_t,
33};
34
35pub fn sys_access(
36 locked: &mut Locked<Unlocked>,
37 current_task: &CurrentTask,
38 user_path: UserCString,
39 mode: u32,
40) -> Result<(), Errno> {
41 sys_faccessat(locked, current_task, FdNumber::AT_FDCWD, user_path, mode)
42}
43
44pub fn sys_alarm(
45 _locked: &mut Locked<Unlocked>,
46 current_task: &CurrentTask,
47 duration: u32,
48) -> Result<u32, Errno> {
49 let duration = zx::MonotonicDuration::from_seconds(duration.into());
50 let new_value = timeval_from_duration(duration);
51 let old_value = current_task.thread_group().set_itimer(
52 current_task,
53 ITIMER_REAL,
54 itimerval { it_value: new_value, it_interval: Default::default() },
55 )?;
56
57 let remaining = duration_from_timeval(old_value.it_value)?;
58
59 let old_value_seconds = remaining.into_seconds();
60 if old_value_seconds == 0 && remaining != zx::MonotonicDuration::default() {
61 return Ok(1);
64 }
65 old_value_seconds.try_into().map_err(|_| errno!(EDOM))
66}
67
68pub fn sys_arch_prctl(
69 _locked: &mut Locked<Unlocked>,
70 current_task: &mut CurrentTask,
71 code: u32,
72 addr: UserAddress,
73) -> Result<(), Errno> {
74 match code {
75 ARCH_SET_FS => {
76 current_task.thread_state.registers.fs_base = addr.ptr() as u64;
77 Ok(())
78 }
79 ARCH_SET_GS => {
80 current_task.thread_state.registers.gs_base = addr.ptr() as u64;
81 Ok(())
82 }
83 _ => {
84 track_stub!(TODO("https://fxbug.dev/322874054"), "arch_prctl", code);
85 error!(ENOSYS)
86 }
87 }
88}
89
90pub fn sys_chmod(
91 locked: &mut Locked<Unlocked>,
92 current_task: &CurrentTask,
93 user_path: UserCString,
94 mode: FileMode,
95) -> Result<(), Errno> {
96 sys_fchmodat(locked, current_task, FdNumber::AT_FDCWD, user_path, mode)
97}
98
99pub fn sys_chown(
100 locked: &mut Locked<Unlocked>,
101 current_task: &CurrentTask,
102 user_path: UserCString,
103 owner: uid_t,
104 group: gid_t,
105) -> Result<(), Errno> {
106 sys_fchownat(locked, current_task, FdNumber::AT_FDCWD, user_path, owner, group, 0)
107}
108
109pub fn sys_clone(
111 locked: &mut Locked<Unlocked>,
112 current_task: &mut CurrentTask,
113 flags: u64,
114 user_stack: UserAddress,
115 user_parent_tid: UserRef<tid_t>,
116 user_child_tid: UserRef<tid_t>,
117 user_tls: UserAddress,
118) -> Result<tid_t, Errno> {
119 do_clone(
122 locked,
123 current_task,
124 &clone_args {
125 flags: flags & !(CSIGNAL as u64),
126 child_tid: user_child_tid.addr().ptr() as u64,
127 parent_tid: user_parent_tid.addr().ptr() as u64,
128 pidfd: user_parent_tid.addr().ptr() as u64,
129 exit_signal: flags & (CSIGNAL as u64),
130 stack: user_stack.ptr() as u64,
131 tls: user_tls.ptr() as u64,
132 ..Default::default()
133 },
134 )
135}
136
137pub fn sys_fork(
138 locked: &mut Locked<Unlocked>,
139 current_task: &mut CurrentTask,
140) -> Result<tid_t, Errno> {
141 do_clone(
142 locked,
143 current_task,
144 &clone_args { exit_signal: uapi::SIGCHLD.into(), ..Default::default() },
145 )
146}
147
148pub fn sys_creat(
150 locked: &mut Locked<Unlocked>,
151 current_task: &CurrentTask,
152 user_path: UserCString,
153 mode: FileMode,
154) -> Result<FdNumber, Errno> {
155 sys_open(
156 locked,
157 current_task,
158 user_path,
159 (OpenFlags::WRONLY | OpenFlags::CREAT | OpenFlags::TRUNC).bits(),
160 mode,
161 )
162}
163
164pub fn sys_dup2(
165 locked: &mut Locked<Unlocked>,
166 current_task: &CurrentTask,
167 oldfd: FdNumber,
168 newfd: FdNumber,
169) -> Result<FdNumber, Errno> {
170 if oldfd == newfd {
171 current_task.get_file_allowing_opath(oldfd)?;
178 return Ok(newfd);
179 }
180 sys_dup3(locked, current_task, oldfd, newfd, 0)
181}
182
183pub fn sys_epoll_create(
184 locked: &mut Locked<Unlocked>,
185 current_task: &CurrentTask,
186 size: i32,
187) -> Result<FdNumber, Errno> {
188 if size < 1 {
189 return error!(EINVAL);
193 }
194 sys_epoll_create1(locked, current_task, 0)
195}
196
197pub fn sys_epoll_wait(
198 locked: &mut Locked<Unlocked>,
199 current_task: &mut CurrentTask,
200 epfd: FdNumber,
201 events: UserRef<EpollEvent>,
202 max_events: i32,
203 timeout: i32,
204) -> Result<usize, Errno> {
205 sys_epoll_pwait(
206 locked,
207 current_task,
208 epfd,
209 events,
210 max_events,
211 timeout,
212 UserRef::<SigSet>::default(),
213 )
214}
215
216pub fn sys_eventfd(
217 locked: &mut Locked<Unlocked>,
218 current_task: &CurrentTask,
219 value: u32,
220) -> Result<FdNumber, Errno> {
221 sys_eventfd2(locked, current_task, value, 0)
222}
223
224pub fn sys_getdents(
225 locked: &mut Locked<Unlocked>,
226 current_task: &CurrentTask,
227 fd: FdNumber,
228 user_buffer: UserAddress,
229 user_capacity: usize,
230) -> Result<usize, Errno> {
231 let file = current_task.get_file(fd)?;
232 let mut offset = file.offset.copy();
233 let mut sink = DirentSink32::new(current_task, &mut *offset, user_buffer, user_capacity);
234 let result = file.readdir(locked, current_task, &mut sink);
235 let ret = sink.map_result_with_actual(result);
236 offset.update();
237 ret
238}
239
240pub fn sys_getpgrp(
241 _locked: &mut Locked<Unlocked>,
242 current_task: &CurrentTask,
243) -> Result<pid_t, Errno> {
244 Ok(current_task.thread_group().read().process_group.leader)
245}
246
247pub fn sys_inotify_init(
248 locked: &mut Locked<Unlocked>,
249 current_task: &CurrentTask,
250) -> Result<FdNumber, Errno> {
251 sys_inotify_init1(locked, current_task, 0)
252}
253
254pub fn sys_lchown(
255 locked: &mut Locked<Unlocked>,
256 current_task: &CurrentTask,
257 user_path: UserCString,
258 owner: uid_t,
259 group: gid_t,
260) -> Result<(), Errno> {
261 sys_fchownat(
262 locked,
263 current_task,
264 FdNumber::AT_FDCWD,
265 user_path,
266 owner,
267 group,
268 AT_SYMLINK_NOFOLLOW,
269 )
270}
271
272pub fn sys_link(
273 locked: &mut Locked<Unlocked>,
274 current_task: &CurrentTask,
275 old_user_path: UserCString,
276 new_user_path: UserCString,
277) -> Result<(), Errno> {
278 sys_linkat(
279 locked,
280 current_task,
281 FdNumber::AT_FDCWD,
282 old_user_path,
283 FdNumber::AT_FDCWD,
284 new_user_path,
285 0,
286 )
287}
288
289pub fn sys_lstat(
290 locked: &mut Locked<Unlocked>,
291 current_task: &CurrentTask,
292 user_path: UserCString,
293 buffer: UserRef<uapi::stat>,
294) -> Result<(), Errno> {
295 sys_newfstatat(
298 locked,
299 current_task,
300 FdNumber::AT_FDCWD,
301 user_path,
302 buffer.into(),
303 AT_SYMLINK_NOFOLLOW,
304 )
305}
306
307pub fn sys_mkdir(
308 locked: &mut Locked<Unlocked>,
309 current_task: &CurrentTask,
310 user_path: UserCString,
311 mode: FileMode,
312) -> Result<(), Errno> {
313 sys_mkdirat(locked, current_task, FdNumber::AT_FDCWD, user_path, mode)
314}
315
316pub fn sys_mknod(
317 locked: &mut Locked<Unlocked>,
318 current_task: &CurrentTask,
319 user_path: UserCString,
320 mode: FileMode,
321 dev: DeviceId,
322) -> Result<(), Errno> {
323 sys_mknodat(locked, current_task, FdNumber::AT_FDCWD, user_path, mode, dev)
324}
325
326pub fn sys_open(
327 locked: &mut Locked<Unlocked>,
328 current_task: &CurrentTask,
329 user_path: UserCString,
330 flags: u32,
331 mode: FileMode,
332) -> Result<FdNumber, Errno> {
333 sys_openat(locked, current_task, FdNumber::AT_FDCWD, user_path, flags, mode)
334}
335
336pub fn sys_pipe(
337 locked: &mut Locked<Unlocked>,
338 current_task: &CurrentTask,
339 user_pipe: UserRef<FdNumber>,
340) -> Result<(), Errno> {
341 sys_pipe2(locked, current_task, user_pipe, 0)
342}
343
344pub fn sys_poll(
345 locked: &mut Locked<Unlocked>,
346 current_task: &mut CurrentTask,
347 user_fds: UserRef<pollfd>,
348 num_fds: i32,
349 timeout: i32,
350) -> Result<usize, Errno> {
351 let deadline = zx::MonotonicInstant::after(duration_from_poll_timeout(timeout)?);
352 poll(locked, current_task, user_fds, num_fds, None, deadline)
353}
354
355pub fn sys_readlink(
356 locked: &mut Locked<Unlocked>,
357 current_task: &CurrentTask,
358 user_path: UserCString,
359 buffer: UserAddress,
360 buffer_size: usize,
361) -> Result<usize, Errno> {
362 sys_readlinkat(locked, current_task, FdNumber::AT_FDCWD, user_path, buffer, buffer_size)
363}
364
365pub fn sys_rmdir(
366 locked: &mut Locked<Unlocked>,
367 current_task: &CurrentTask,
368 user_path: UserCString,
369) -> Result<(), Errno> {
370 sys_unlinkat(locked, current_task, FdNumber::AT_FDCWD, user_path, AT_REMOVEDIR)
371}
372
373pub fn sys_rename(
374 locked: &mut Locked<Unlocked>,
375 current_task: &CurrentTask,
376 old_user_path: UserCString,
377 new_user_path: UserCString,
378) -> Result<(), Errno> {
379 sys_renameat2(
380 locked,
381 current_task,
382 FdNumber::AT_FDCWD,
383 old_user_path,
384 FdNumber::AT_FDCWD,
385 new_user_path,
386 0,
387 )
388}
389
390pub fn sys_renameat(
391 locked: &mut Locked<Unlocked>,
392 current_task: &CurrentTask,
393 old_dir_fd: FdNumber,
394 old_user_path: UserCString,
395 new_dir_fd: FdNumber,
396 new_user_path: UserCString,
397) -> Result<(), Errno> {
398 sys_renameat2(locked, current_task, old_dir_fd, old_user_path, new_dir_fd, new_user_path, 0)
399}
400
401pub fn sys_stat(
402 locked: &mut Locked<Unlocked>,
403 current_task: &CurrentTask,
404 user_path: UserCString,
405 buffer: UserRef<uapi::stat>,
406) -> Result<(), Errno> {
407 sys_newfstatat(locked, current_task, FdNumber::AT_FDCWD, user_path, buffer.into(), 0)
410}
411
412pub fn sys_symlink(
414 locked: &mut Locked<Unlocked>,
415 current_task: &CurrentTask,
416 user_target: UserCString,
417 user_path: UserCString,
418) -> Result<(), Errno> {
419 sys_symlinkat(locked, current_task, user_target, FdNumber::AT_FDCWD, user_path)
420}
421
422pub fn sys_time(
423 _locked: &mut Locked<Unlocked>,
424 current_task: &CurrentTask,
425 time_addr: UserRef<__kernel_time_t>,
426) -> Result<__kernel_time_t, Errno> {
427 let time = (utc::utc_now().into_nanos() / zx::MonotonicDuration::from_seconds(1).into_nanos())
428 as __kernel_time_t;
429 if !time_addr.is_null() {
430 current_task.write_object(time_addr, &time)?;
431 }
432 Ok(time)
433}
434
435pub fn sys_unlink(
436 locked: &mut Locked<Unlocked>,
437 current_task: &CurrentTask,
438 user_path: UserCString,
439) -> Result<(), Errno> {
440 sys_unlinkat(locked, current_task, FdNumber::AT_FDCWD, user_path, 0)
441}
442
443pub fn sys_signalfd(
444 locked: &mut Locked<Unlocked>,
445 current_task: &CurrentTask,
446 fd: FdNumber,
447 mask_addr: UserRef<SigSet>,
448 mask_size: usize,
449) -> Result<FdNumber, Errno> {
450 sys_signalfd4(locked, current_task, fd, mask_addr, mask_size, 0)
451}
452
453pub fn sys_vfork(
454 locked: &mut Locked<Unlocked>,
455 current_task: &mut CurrentTask,
456) -> Result<tid_t, Errno> {
457 do_clone(
458 locked,
459 current_task,
460 &clone_args {
461 flags: (CLONE_VFORK | CLONE_VM) as u64,
462 exit_signal: SIGCHLD.number() as u64,
463 ..Default::default()
464 },
465 )
466}
467
468#[cfg(test)]
469mod tests {
470 use super::*;
471 use crate::mm::{MemoryAccessor, PAGE_SIZE};
472 use crate::testing::{map_memory, spawn_kernel_and_run, spawn_kernel_and_run_with_pkgfs};
473 use crate::vfs::FdFlags;
474
475 #[::fuchsia::test]
476 async fn test_sys_dup2() {
477 spawn_kernel_and_run_with_pkgfs(async |locked, current_task| {
479 let fd = FdNumber::from_raw(42);
480 assert_eq!(sys_dup2(locked, current_task, fd, fd), error!(EBADF));
481 let file_handle = current_task
482 .open_file(locked, "data/testfile.txt".into(), OpenFlags::RDONLY)
483 .expect("open_file");
484 let fd = current_task.add_file(locked, file_handle, FdFlags::empty()).expect("add");
485 assert_eq!(sys_dup2(locked, current_task, fd, fd), Ok(fd));
486 })
487 .await;
488 }
489
490 #[::fuchsia::test]
491 async fn test_sys_creat() {
492 spawn_kernel_and_run(async |locked, current_task| {
493 let path_addr = map_memory(locked, current_task, UserAddress::default(), *PAGE_SIZE);
494 let path = "newfile.txt";
495 current_task.write_memory(path_addr, path.as_bytes()).unwrap();
496 let fd = sys_creat(
497 locked,
498 current_task,
499 UserCString::new(current_task, path_addr),
500 FileMode::default(),
501 )
502 .unwrap();
503 let _file_handle =
504 current_task.open_file(locked, path.into(), OpenFlags::RDONLY).unwrap();
505 assert!(
506 !current_task
507 .live()
508 .files
509 .get_fd_flags_allowing_opath(fd)
510 .unwrap()
511 .contains(FdFlags::CLOEXEC)
512 );
513 })
514 .await;
515 }
516
517 #[::fuchsia::test]
518 async fn test_time() {
519 spawn_kernel_and_run(async |locked, current_task| {
520 let time1 = sys_time(locked, ¤t_task, Default::default()).expect("time");
521 assert!(time1 > 0);
522 let address = map_memory(
523 locked,
524 ¤t_task,
525 UserAddress::default(),
526 std::mem::size_of::<__kernel_time_t>() as u64,
527 );
528 std::thread::sleep(std::time::Duration::from_secs(2));
529 let time2 = sys_time(locked, ¤t_task, address.into()).expect("time");
530 assert!(time2 >= time1 + 2);
531 assert!(time2 < time1 + 10);
532 let time3: __kernel_time_t =
533 current_task.read_object(address.into()).expect("read_object");
534 assert_eq!(time2, time3);
535 })
536 .await;
537 }
538}