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_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        // We can't return a zero value if the alarm was scheduled even if it had
56        // less than one second remaining. Return 1 instead.
57        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    // From `arch_prctl(2)`:
68    // EPERM: `addr` is outside the process address space.
69    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), &current_task.thread_state.registers.fs_base)?;
87            Ok(())
88        }
89        ARCH_GET_GS => {
90            current_task
91                .write_object(UserRef::new(addr), &current_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
118/// The parameter order for `clone` varies by architecture.
119pub 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    // Our flags parameter uses the low 8 bits (CSIGNAL mask) of flags to indicate the exit
128    // signal. The CloneArgs struct separates these as `flags` and `exit_signal`.
129    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
148// https://pubs.opengroup.org/onlinepubs/9699919799/functions/creat.html
149pub 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        // O_PATH allowed for:
169        //
170        //  Duplicating the file descriptor (dup(2), fcntl(2)
171        //  F_DUPFD, etc.).
172        //
173        // See https://man7.org/linux/man-pages/man2/open.2.html
174        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        // The man page for epoll_create says the size was used in a previous implementation as
183        // a hint but no longer does anything. But it's still required to be >= 1 to ensure
184        // programs are backwards-compatible.
185        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    // TODO(https://fxbug.dev/42172993): Add the `AT_NO_AUTOMOUNT` flag once it is supported in
253    // `sys_newfstatat`.
254    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    // TODO(https://fxbug.dev/42172993): Add the `AT_NO_AUTOMOUNT` flag once it is supported in
341    // `sys_newfstatat`.
342    sys_newfstatat(current_task, FdNumber::AT_FDCWD, user_path, buffer.into(), 0)
343}
344
345// https://man7.org/linux/man-pages/man2/symlink.2.html
346pub 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        // Most tests are handled by test_sys_dup3, only test the case where both fds are equals.
400        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(&current_task, Default::default()).expect("time");
440            assert!(time1 > 0);
441            let address = map_memory(
442                &current_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(&current_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}