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