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