Skip to main content

starnix_core/arch/x64/
syscalls.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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        // We can't return a zero value if the alarm was scheduled even if it had
55        // less than one second remaining. Return 1 instead.
56        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
99/// The parameter order for `clone` varies by architecture.
100pub 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    // Our flags parameter uses the low 8 bits (CSIGNAL mask) of flags to indicate the exit
109    // signal. The CloneArgs struct separates these as `flags` and `exit_signal`.
110    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
129// https://pubs.opengroup.org/onlinepubs/9699919799/functions/creat.html
130pub 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        // O_PATH allowed for:
150        //
151        //  Duplicating the file descriptor (dup(2), fcntl(2)
152        //  F_DUPFD, etc.).
153        //
154        // See https://man7.org/linux/man-pages/man2/open.2.html
155        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        // The man page for epoll_create says the size was used in a previous implementation as
164        // a hint but no longer does anything. But it's still required to be >= 1 to ensure
165        // programs are backwards-compatible.
166        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    // TODO(https://fxbug.dev/42172993): Add the `AT_NO_AUTOMOUNT` flag once it is supported in
234    // `sys_newfstatat`.
235    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    // TODO(https://fxbug.dev/42172993): Add the `AT_NO_AUTOMOUNT` flag once it is supported in
322    // `sys_newfstatat`.
323    sys_newfstatat(current_task, FdNumber::AT_FDCWD, user_path, buffer.into(), 0)
324}
325
326// https://man7.org/linux/man-pages/man2/symlink.2.html
327pub 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        // Most tests are handled by test_sys_dup3, only test the case where both fds are equals.
381        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(&current_task, Default::default()).expect("time");
421            assert!(time1 > 0);
422            let address = map_memory(
423                &current_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(&current_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}