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 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        // We can't return a zero value if the alarm was scheduled even if it had
62        // less than one second remaining. Return 1 instead.
63        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
109/// The parameter order for `clone` varies by architecture.
110pub 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    // Our flags parameter uses the low 8 bits (CSIGNAL mask) of flags to indicate the exit
120    // signal. The CloneArgs struct separates these as `flags` and `exit_signal`.
121    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
148// https://pubs.opengroup.org/onlinepubs/9699919799/functions/creat.html
149pub 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        // O_PATH allowed for:
172        //
173        //  Duplicating the file descriptor (dup(2), fcntl(2)
174        //  F_DUPFD, etc.).
175        //
176        // See https://man7.org/linux/man-pages/man2/open.2.html
177        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        // The man page for epoll_create says the size was used in a previous implementation as
190        // a hint but no longer does anything. But it's still required to be >= 1 to ensure
191        // programs are backwards-compatible.
192        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    // TODO(https://fxbug.dev/42172993): Add the `AT_NO_AUTOMOUNT` flag once it is supported in
296    // `sys_newfstatat`.
297    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    // TODO(https://fxbug.dev/42172993): Add the `AT_NO_AUTOMOUNT` flag once it is supported in
408    // `sys_newfstatat`.
409    sys_newfstatat(locked, current_task, FdNumber::AT_FDCWD, user_path, buffer.into(), 0)
410}
411
412// https://man7.org/linux/man-pages/man2/symlink.2.html
413pub 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        // Most tests are handled by test_sys_dup3, only test the case where both fds are equals.
478        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, &current_task, Default::default()).expect("time");
521            assert!(time1 > 0);
522            let address = map_memory(
523                locked,
524                &current_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, &current_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}