Skip to main content

starnix_core/vfs/
syscalls.rs

1// Copyright 2021 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::{IOVecPtr, MemoryAccessor, MemoryAccessorExt, PAGE_SIZE};
6use crate::security;
7use crate::syscalls::time::{ITimerSpecPtr, TimeSpecPtr, TimeValPtr};
8use crate::task::{CurrentTask, EventHandler, ProcessEntryRef, ReadyItem, ReadyItemKey, Waiter};
9use crate::time::{Timeline, TimerWakeup};
10use crate::vfs::aio::AioContext;
11use crate::vfs::buffers::{UserBuffersInputBuffer, UserBuffersOutputBuffer};
12use crate::vfs::eventfd::{EventFdType, new_eventfd};
13use crate::vfs::fs_args::MountParams;
14use crate::vfs::pidfd::new_pidfd;
15use crate::vfs::pipe::{PipeFileObject, new_pipe};
16use crate::vfs::timer::TimerFile;
17use crate::vfs::{
18    CheckAccessReason, DirentSink64, EpollFileObject, FallocMode, FdFlags, FdNumber,
19    FileAsyncOwner, FileHandle, FileSystemOptions, FlockOperation, FsStr, FsString, LookupContext,
20    Mount, NamespaceNode, PathWithReachability, RecordLockCommand, RenameFlags, SeekTarget,
21    StatxFlags, SymlinkMode, SymlinkTarget, TargetFdNumber, TimeUpdateType, UnlinkKind,
22    ValueOrSize, WhatToMount, XattrOp, checked_add_offset_and_length, new_memfd, new_zombie_pidfd,
23    splice,
24};
25use starnix_logging::{log_trace, track_stub};
26use starnix_sync::{EventHandlerReadyQueueLock, LockDepMutex};
27use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
28use starnix_types::time::{
29    duration_from_poll_timeout, duration_from_timespec, time_from_timespec, timespec_from_duration,
30};
31use starnix_types::user_buffer::UserBuffer;
32use starnix_uapi::auth::{
33    CAP_BLOCK_SUSPEND, CAP_DAC_READ_SEARCH, CAP_LEASE, CAP_SYS_ADMIN, CAP_WAKE_ALARM, Capabilities,
34    Credentials, PTRACE_MODE_ATTACH_REALCREDS,
35};
36use starnix_uapi::device_id::DeviceId;
37use starnix_uapi::errors::{
38    EFAULT, EINTR, ENAMETOOLONG, ENOTSUP, ETIMEDOUT, Errno, ErrnoResultExt,
39};
40use starnix_uapi::file_lease::FileLeaseType;
41use starnix_uapi::file_mode::{Access, AccessCheck, FileMode};
42use starnix_uapi::inotify_mask::InotifyMask;
43use starnix_uapi::mount_flags::MountFlags;
44use starnix_uapi::open_flags::OpenFlags;
45use starnix_uapi::personality::PersonalityFlags;
46use starnix_uapi::resource_limits::Resource;
47use starnix_uapi::seal_flags::SealFlags;
48use starnix_uapi::signals::SigSet;
49use starnix_uapi::unmount_flags::UnmountFlags;
50use starnix_uapi::user_address::{MultiArchUserRef, UserAddress, UserCString, UserRef};
51use starnix_uapi::user_value::UserValue;
52use starnix_uapi::vfs::{EpollEvent, FdEvents, ResolveFlags};
53use starnix_uapi::{
54    __kernel_fd_set, AT_EACCESS, AT_EMPTY_PATH, AT_NO_AUTOMOUNT, AT_REMOVEDIR, AT_SYMLINK_FOLLOW,
55    AT_SYMLINK_NOFOLLOW, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM, CLOCK_MONOTONIC, CLOCK_REALTIME,
56    CLOCK_REALTIME_ALARM, CLOSE_RANGE_CLOEXEC, CLOSE_RANGE_UNSHARE, EFD_CLOEXEC, EFD_NONBLOCK,
57    EFD_SEMAPHORE, EPOLL_CLOEXEC, EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD, F_ADD_SEALS,
58    F_DUPFD, F_DUPFD_CLOEXEC, F_GET_SEALS, F_GETFD, F_GETFL, F_GETLEASE, F_GETLK, F_GETLK64,
59    F_GETOWN, F_GETOWN_EX, F_OFD_GETLK, F_OFD_SETLK, F_OFD_SETLKW, F_OWNER_PGRP, F_OWNER_PID,
60    F_OWNER_TID, F_SETFD, F_SETFL, F_SETLEASE, F_SETLK, F_SETLK64, F_SETLKW, F_SETLKW64, F_SETOWN,
61    F_SETOWN_EX, F_SETSIG, FIOCLEX, FIONCLEX, MFD_ALLOW_SEALING, MFD_CLOEXEC, MFD_EXEC,
62    MFD_HUGE_MASK, MFD_HUGE_SHIFT, MFD_HUGETLB, MFD_NOEXEC_SEAL, NAME_MAX, O_CLOEXEC, O_CREAT,
63    O_NOFOLLOW, O_PATH, O_TMPFILE, PIDFD_NONBLOCK, POLLERR, POLLHUP, POLLIN, POLLOUT, POLLPRI,
64    POLLRDBAND, POLLRDNORM, POLLWRBAND, POLLWRNORM, POSIX_FADV_DONTNEED, POSIX_FADV_NOREUSE,
65    POSIX_FADV_NORMAL, POSIX_FADV_RANDOM, POSIX_FADV_SEQUENTIAL, POSIX_FADV_WILLNEED,
66    RWF_SUPPORTED, TFD_CLOEXEC, TFD_NONBLOCK, TFD_TIMER_ABSTIME, TFD_TIMER_CANCEL_ON_SET,
67    XATTR_CREATE, XATTR_NAME_MAX, XATTR_REPLACE, aio_context_t, errno, error, f_owner_ex, io_event,
68    iocb, off_t, pid_t, pollfd, pselect6_sigmask, sigset_t, statx, timespec, uapi, uid_t,
69};
70use std::cmp::Ordering;
71use std::collections::VecDeque;
72use std::marker::PhantomData;
73use std::sync::Arc;
74use std::usize;
75use zerocopy::{Immutable, IntoBytes};
76
77uapi::check_arch_independent_layout! {
78    pollfd {
79        fd,
80        events,
81        revents,
82    }
83
84    io_event {
85        data,
86        obj,
87        res,
88        res2,
89    }
90
91    iocb {
92        aio_data,
93        aio_key,
94        aio_rw_flags,
95        aio_lio_opcode,
96        aio_reqprio,
97        aio_fildes,
98        aio_buf,
99        aio_nbytes,
100        aio_offset,
101        aio_reserved2,
102        aio_flags,
103        aio_resfd,
104    }
105
106    statx_timestamp {
107        tv_sec,
108        tv_nsec,
109    }
110
111    statx {
112        stx_mask,
113        stx_blksize,
114        stx_attributes,
115        stx_nlink,
116        stx_uid,
117        stx_gid,
118        stx_mode,
119        stx_ino,
120        stx_size,
121        stx_blocks,
122        stx_attributes_mask,
123        stx_atime,
124        stx_btime,
125        stx_ctime,
126        stx_mtime,
127        stx_rdev_major,
128        stx_rdev_minor,
129        stx_dev_major,
130        stx_dev_minor,
131        stx_mnt_id,
132        stx_dio_mem_align,
133        stx_dio_offset_align,
134        stx_subvol,
135        stx_atomic_write_unit_min,
136        stx_atomic_write_unit_max,
137        stx_atomic_write_segments_max,
138    }
139
140    io_sqring_offsets {
141        head,
142        tail,
143        ring_mask,
144        ring_entries,
145        flags,
146        dropped,
147        array,
148        resv1,
149        user_addr,
150    }
151
152    io_cqring_offsets {
153        head,
154        tail,
155        ring_mask,
156        ring_entries,
157        overflow,
158        cqes,
159        flags,
160        resv1,
161        user_addr,
162    }
163
164    io_uring_params {
165        sq_entries,
166        cq_entries,
167        flags,
168        sq_thread_cpu,
169        sq_thread_idle,
170        features,
171        wq_fd,
172        resv,
173        sq_off,
174        cq_off,
175    }
176
177    io_uring_rsrc_update {
178        offset,
179        resv,
180        data,
181    }
182
183    io_uring_buf_reg {
184        ring_addr,
185        ring_entries,
186        bgid,
187        flags,
188        resv,
189    }
190}
191
192// Constants from bionic/libc/include/sys/stat.h
193const UTIME_NOW: i64 = 0x3fffffff;
194const UTIME_OMIT: i64 = 0x3ffffffe;
195
196pub type OffsetPtr = MultiArchUserRef<uapi::off_t, uapi::arch32::off_t>;
197pub type IocbPtr = MultiArchUserRef<iocb, iocb>;
198pub type IocbPtrPtr = MultiArchUserRef<IocbPtr, IocbPtr>;
199
200pub fn sys_read(
201    current_task: &CurrentTask,
202    fd: FdNumber,
203    address: UserAddress,
204    length: usize,
205) -> Result<usize, Errno> {
206    let file = current_task.files().get(fd)?;
207    file.read(
208        current_task,
209        &mut UserBuffersOutputBuffer::unified_new_at(current_task, address, length)?,
210    )
211    .map_eintr(|| errno!(ERESTARTSYS))
212}
213
214pub fn sys_write(
215    current_task: &CurrentTask,
216    fd: FdNumber,
217    address: UserAddress,
218    length: usize,
219) -> Result<usize, Errno> {
220    let file = current_task.files().get(fd)?;
221    file.write(
222        current_task,
223        &mut UserBuffersInputBuffer::unified_new_at(current_task, address, length)?,
224    )
225    .map_eintr(|| errno!(ERESTARTSYS))
226}
227
228pub fn sys_close(current_task: &CurrentTask, fd: FdNumber) -> Result<(), Errno> {
229    current_task.files().close(fd)?;
230    Ok(())
231}
232
233pub fn sys_close_range(
234    current_task: &CurrentTask,
235    first: u32,
236    last: u32,
237    flags: u32,
238) -> Result<(), Errno> {
239    if first > last || flags & !(CLOSE_RANGE_UNSHARE | CLOSE_RANGE_CLOEXEC) != 0 {
240        return error!(EINVAL);
241    }
242    if flags & CLOSE_RANGE_UNSHARE != 0 {
243        current_task.running_state().unshare_files(current_task);
244    }
245    let files = current_task.files();
246    let in_range = |fd: FdNumber| fd.raw() as u32 >= first && fd.raw() as u32 <= last;
247    if flags & CLOSE_RANGE_CLOEXEC != 0 {
248        files.retain(|fd, flags| {
249            if in_range(fd) {
250                *flags |= FdFlags::CLOEXEC;
251            }
252            true
253        });
254    } else {
255        files.retain(|fd, _| !in_range(fd));
256    }
257    Ok(())
258}
259
260pub fn sys_lseek(
261    current_task: &CurrentTask,
262    fd: FdNumber,
263    offset: off_t,
264    whence: u32,
265) -> Result<off_t, Errno> {
266    let file = current_task.files().get(fd)?;
267    file.seek(current_task, SeekTarget::from_raw(whence, offset)?)
268}
269
270pub fn sys_fcntl(
271    current_task: &CurrentTask,
272    fd: FdNumber,
273    cmd: u32,
274    arg: u64,
275) -> Result<SyscallResult, Errno> {
276    let file = match cmd {
277        F_DUPFD | F_DUPFD_CLOEXEC | F_GETFD | F_SETFD | F_GETFL => {
278            current_task.files().get_allowing_opath(fd)?
279        }
280        _ => current_task.files().get(fd)?,
281    };
282
283    security::check_file_fcntl_access(current_task, &file, cmd, arg)?;
284
285    match cmd {
286        F_DUPFD | F_DUPFD_CLOEXEC => {
287            let fd_number = arg as i32;
288            let flags = if cmd == F_DUPFD_CLOEXEC { FdFlags::CLOEXEC } else { FdFlags::empty() };
289            let newfd = current_task.files().duplicate(
290                current_task,
291                fd,
292                TargetFdNumber::Minimum(FdNumber::from_raw(fd_number)),
293                flags,
294            )?;
295            Ok(newfd.into())
296        }
297        F_GETOWN => match file.get_async_owner() {
298            FileAsyncOwner::Unowned => Ok(0.into()),
299            FileAsyncOwner::Thread(tid) => Ok(tid.into()),
300            FileAsyncOwner::Process(pid) => Ok(pid.into()),
301            FileAsyncOwner::ProcessGroup(pgid) => Ok((-pgid).into()),
302        },
303        F_GETOWN_EX => {
304            let owner = match file.get_async_owner() {
305                FileAsyncOwner::Unowned => uapi::f_owner_ex { type_: F_OWNER_TID as i32, pid: 0 },
306                FileAsyncOwner::Thread(tid) => {
307                    uapi::f_owner_ex { type_: F_OWNER_TID as i32, pid: tid }
308                }
309                FileAsyncOwner::Process(pid) => uapi::f_owner_ex { type_: F_OWNER_PID as i32, pid },
310                FileAsyncOwner::ProcessGroup(pgid) => {
311                    uapi::f_owner_ex { type_: F_OWNER_PGRP as i32, pid: pgid }
312                }
313            };
314            let user_owner: UserRef<f_owner_ex> =
315                UserRef::<uapi::f_owner_ex>::new(UserAddress::from(arg));
316            current_task.write_object(user_owner, &owner)?;
317            Ok(SUCCESS)
318        }
319        F_SETOWN => {
320            let pid = (arg as u32) as i32;
321            let owner = match pid.cmp(&0) {
322                Ordering::Equal => FileAsyncOwner::Unowned,
323                Ordering::Greater => FileAsyncOwner::Process(pid),
324                Ordering::Less => {
325                    FileAsyncOwner::ProcessGroup(pid.checked_neg().ok_or_else(|| errno!(EINVAL))?)
326                }
327            };
328            owner.validate(current_task)?;
329            // TODO: https://fxbug.dev/364569860 - Integrate with LSM file_setfowner hook.
330            file.set_async_owner(owner);
331            Ok(SUCCESS)
332        }
333        F_SETOWN_EX => {
334            let user_owner = UserRef::<uapi::f_owner_ex>::new(UserAddress::from(arg));
335            let requested_owner = current_task.read_object(user_owner)?;
336            let owner = match requested_owner.type_ as u32 {
337                F_OWNER_TID => FileAsyncOwner::Thread(requested_owner.pid),
338                F_OWNER_PID => FileAsyncOwner::Process(requested_owner.pid),
339                F_OWNER_PGRP => FileAsyncOwner::ProcessGroup(requested_owner.pid),
340                _ => return error!(EINVAL),
341            };
342            owner.validate(current_task)?;
343            file.set_async_owner(owner);
344            Ok(SUCCESS)
345        }
346        F_GETFD => Ok(current_task.files().get_fd_flags_allowing_opath(fd)?.into()),
347        F_SETFD => {
348            current_task
349                .files()
350                .set_fd_flags_allowing_opath(fd, FdFlags::from_bits_truncate(arg as u32))?;
351            Ok(SUCCESS)
352        }
353        F_GETFL => {
354            // O_PATH allowed for:
355            //
356            //   Retrieving open file status flags using the fcntl(2)
357            //   F_GETFL operation: the returned flags will include the
358            //   bit O_PATH.
359            //
360            // See https://man7.org/linux/man-pages/man2/open.2.html
361            Ok(file.flags().into())
362        }
363        F_SETFL => {
364            let settable_flags = OpenFlags::APPEND
365                | OpenFlags::DIRECT
366                | OpenFlags::NOATIME
367                | OpenFlags::NONBLOCK
368                | OpenFlags::ASYNC;
369            let requested_flags =
370                OpenFlags::from_bits_truncate((arg as u32) & settable_flags.bits());
371
372            // If `NOATIME` flag is being set then check that it's allowed.
373            if requested_flags.contains(OpenFlags::NOATIME)
374                && !file.flags().contains(OpenFlags::NOATIME)
375            {
376                file.name.check_o_noatime_allowed(current_task)?;
377            }
378
379            file.update_file_flags(requested_flags, settable_flags);
380            Ok(SUCCESS)
381        }
382        F_SETLK | F_SETLKW | F_GETLK => {
383            let flock_ref =
384                MultiArchUserRef::<uapi::flock, uapi::arch32::flock>::new(current_task, arg);
385            let flock = current_task.read_multi_arch_object(flock_ref)?;
386            let cmd = RecordLockCommand::from_raw(cmd).ok_or_else(|| errno!(EINVAL))?;
387            if let Some(flock) = file.record_lock(current_task, cmd, flock)? {
388                current_task.write_multi_arch_object(flock_ref, flock)?;
389            }
390            Ok(SUCCESS)
391        }
392        F_SETLK64 | F_SETLKW64 | F_GETLK64 | F_OFD_GETLK | F_OFD_SETLK | F_OFD_SETLKW => {
393            let flock_ref =
394                MultiArchUserRef::<uapi::flock, uapi::arch32::flock64>::new(current_task, arg);
395            let flock = current_task.read_multi_arch_object(flock_ref)?;
396            let cmd = RecordLockCommand::from_raw(cmd).ok_or_else(|| errno!(EINVAL))?;
397            if let Some(flock) = file.record_lock(current_task, cmd, flock)? {
398                current_task.write_multi_arch_object(flock_ref, flock)?;
399            }
400            Ok(SUCCESS)
401        }
402        F_ADD_SEALS => {
403            if !file.can_write() {
404                // Cannot add seals if the file is not writable
405                return error!(EPERM);
406            }
407            let mut state = file.name.entry.node.write_guard_state.lock();
408            let flags = SealFlags::from_bits_truncate(arg as u32);
409            state.try_add_seal(flags)?;
410            Ok(SUCCESS)
411        }
412        F_GET_SEALS => {
413            let state = file.name.entry.node.write_guard_state.lock();
414            Ok(state.get_seals()?.into())
415        }
416        F_SETLEASE => {
417            let fsuid = current_task.current_creds().fsuid;
418            if fsuid != file.node().info().uid {
419                security::check_task_capable(current_task, CAP_LEASE)?;
420            }
421            let lease = FileLeaseType::from_bits(arg as u32)?;
422            file.set_lease(current_task, lease)?;
423            Ok(SUCCESS)
424        }
425        F_GETLEASE => Ok(file.get_lease().into()),
426        F_SETSIG => {
427            track_stub!(TODO("https://fxbug.dev/437972675"), "F_SETSIG");
428            return error!(EINVAL);
429        }
430        _ => file.fcntl(current_task, cmd, arg),
431    }
432}
433
434pub fn sys_pread64(
435    current_task: &CurrentTask,
436    fd: FdNumber,
437    address: UserAddress,
438    length: usize,
439    offset: off_t,
440) -> Result<usize, Errno> {
441    let file = current_task.files().get(fd)?;
442    let offset = offset.try_into().map_err(|_| errno!(EINVAL))?;
443    file.read_at(
444        current_task,
445        offset,
446        &mut UserBuffersOutputBuffer::unified_new_at(current_task, address, length)?,
447    )
448}
449
450pub fn sys_pwrite64(
451    current_task: &CurrentTask,
452    fd: FdNumber,
453    address: UserAddress,
454    length: usize,
455    offset: off_t,
456) -> Result<usize, Errno> {
457    let file = current_task.files().get(fd)?;
458    let offset = offset.try_into().map_err(|_| errno!(EINVAL))?;
459    file.write_at(
460        current_task,
461        offset,
462        &mut UserBuffersInputBuffer::unified_new_at(current_task, address, length)?,
463    )
464}
465
466fn do_readv(
467    current_task: &CurrentTask,
468    fd: FdNumber,
469    iovec_addr: IOVecPtr,
470    iovec_count: UserValue<i32>,
471    offset: Option<off_t>,
472    flags: u32,
473) -> Result<usize, Errno> {
474    if flags & !RWF_SUPPORTED != 0 {
475        return error!(EOPNOTSUPP);
476    }
477    if flags != 0 {
478        track_stub!(TODO("https://fxbug.dev/322875072"), "preadv2 flags", flags);
479    }
480    let file = current_task.files().get(fd)?;
481    let iovec = current_task.read_iovec(iovec_addr, iovec_count)?;
482    let mut data = UserBuffersOutputBuffer::unified_new(current_task, iovec)?;
483    if let Some(offset) = offset {
484        file.read_at(current_task, offset.try_into().map_err(|_| errno!(EINVAL))?, &mut data)
485    } else {
486        file.read(current_task, &mut data)
487    }
488}
489
490pub fn sys_readv(
491    current_task: &CurrentTask,
492    fd: FdNumber,
493    iovec_addr: IOVecPtr,
494    iovec_count: UserValue<i32>,
495) -> Result<usize, Errno> {
496    do_readv(current_task, fd, iovec_addr, iovec_count, None, 0)
497}
498
499pub fn sys_preadv(
500    current_task: &CurrentTask,
501    fd: FdNumber,
502    iovec_addr: IOVecPtr,
503    iovec_count: UserValue<i32>,
504    offset: off_t,
505) -> Result<usize, Errno> {
506    do_readv(current_task, fd, iovec_addr, iovec_count, Some(offset), 0)
507}
508
509pub fn sys_preadv2(
510    current_task: &CurrentTask,
511    fd: FdNumber,
512    iovec_addr: IOVecPtr,
513    iovec_count: UserValue<i32>,
514    offset: off_t,
515    _unused: SyscallArg, // On 32-bit systems, holds the upper 32 bits of offset.
516    flags: u32,
517) -> Result<usize, Errno> {
518    let offset = if offset == -1 { None } else { Some(offset) };
519    do_readv(current_task, fd, iovec_addr, iovec_count, offset, flags)
520}
521
522fn do_writev(
523    current_task: &CurrentTask,
524    fd: FdNumber,
525    iovec_addr: IOVecPtr,
526    iovec_count: UserValue<i32>,
527    offset: Option<off_t>,
528    flags: u32,
529) -> Result<usize, Errno> {
530    if flags & !RWF_SUPPORTED != 0 {
531        return error!(EOPNOTSUPP);
532    }
533    if flags != 0 {
534        track_stub!(TODO("https://fxbug.dev/322874523"), "pwritev2 flags", flags);
535    }
536
537    let file = current_task.files().get(fd)?;
538    let iovec = current_task.read_iovec(iovec_addr, iovec_count)?;
539    let mut data = UserBuffersInputBuffer::unified_new(current_task, iovec)?;
540    let res = if let Some(offset) = offset {
541        file.write_at(current_task, offset.try_into().map_err(|_| errno!(EINVAL))?, &mut data)
542    } else {
543        file.write(current_task, &mut data)
544    };
545
546    match &res {
547        Err(e) if e.code == EFAULT => {
548            track_stub!(TODO("https://fxbug.dev/297370529"), "allow partial writes")
549        }
550        _ => (),
551    }
552
553    res
554}
555
556pub fn sys_writev(
557    current_task: &CurrentTask,
558    fd: FdNumber,
559    iovec_addr: IOVecPtr,
560    iovec_count: UserValue<i32>,
561) -> Result<usize, Errno> {
562    do_writev(current_task, fd, iovec_addr, iovec_count, None, 0)
563}
564
565pub fn sys_pwritev(
566    current_task: &CurrentTask,
567    fd: FdNumber,
568    iovec_addr: IOVecPtr,
569    iovec_count: UserValue<i32>,
570    offset: off_t,
571) -> Result<usize, Errno> {
572    do_writev(current_task, fd, iovec_addr, iovec_count, Some(offset), 0)
573}
574
575pub fn sys_pwritev2(
576    current_task: &CurrentTask,
577    fd: FdNumber,
578    iovec_addr: IOVecPtr,
579    iovec_count: UserValue<i32>,
580    offset: off_t,
581    _unused: SyscallArg, // On 32-bit systems, holds the upper 32 bits of offset.
582    flags: u32,
583) -> Result<usize, Errno> {
584    let offset = if offset == -1 { None } else { Some(offset) };
585    do_writev(current_task, fd, iovec_addr, iovec_count, offset, flags)
586}
587
588type StatFsPtr = MultiArchUserRef<uapi::statfs, uapi::arch32::statfs>;
589
590pub fn fstatfs<T32: IntoBytes + Immutable + TryFrom<uapi::statfs>>(
591    current_task: &CurrentTask,
592    fd: FdNumber,
593    user_buf: MultiArchUserRef<uapi::statfs, T32>,
594) -> Result<(), Errno> {
595    // O_PATH allowed for:
596    //
597    //   fstatfs(2) (since Linux 3.12).
598    //
599    // See https://man7.org/linux/man-pages/man2/open.2.html
600    let file = current_task.files().get_allowing_opath(fd)?;
601    let mut stat = file.fs.statfs(current_task)?;
602    stat.f_flags |= file.name.mount.flags().bits() as i64;
603    current_task.write_multi_arch_object(user_buf, stat)?;
604    Ok(())
605}
606
607pub fn sys_fstatfs(
608    current_task: &CurrentTask,
609    fd: FdNumber,
610    user_buf: StatFsPtr,
611) -> Result<(), Errno> {
612    fstatfs(current_task, fd, user_buf)
613}
614
615fn statfs<T32: IntoBytes + Immutable + TryFrom<uapi::statfs>>(
616    current_task: &CurrentTask,
617    user_path: UserCString,
618    user_buf: MultiArchUserRef<uapi::statfs, T32>,
619) -> Result<(), Errno> {
620    let name = lookup_at(current_task, FdNumber::AT_FDCWD, user_path, LookupFlags::default())?;
621    let fs = name.entry.node.fs();
622    let mut stat = fs.statfs(current_task)?;
623    stat.f_flags |= name.mount.flags().bits() as i64;
624    current_task.write_multi_arch_object(user_buf, stat)?;
625    Ok(())
626}
627
628pub fn sys_statfs(
629    current_task: &CurrentTask,
630    user_path: UserCString,
631    user_buf: StatFsPtr,
632) -> Result<(), Errno> {
633    statfs(current_task, user_path, user_buf)
634}
635
636pub fn sys_sendfile(
637    current_task: &CurrentTask,
638    out_fd: FdNumber,
639    in_fd: FdNumber,
640    user_offset: OffsetPtr,
641    count: i32,
642) -> Result<usize, Errno> {
643    splice::sendfile(current_task, out_fd, in_fd, user_offset, count)
644}
645
646/// A convenient wrapper for Task::open_file_at.
647///
648/// Reads user_path from user memory and then calls through to Task::open_file_at.
649fn open_file_at(
650    current_task: &CurrentTask,
651    dir_fd: FdNumber,
652    user_path: UserCString,
653    flags: u32,
654    mode: FileMode,
655    resolve_flags: ResolveFlags,
656) -> Result<FileHandle, Errno> {
657    let path = current_task.read_path(user_path)?;
658    log_trace!(dir_fd:%, path:%; "open_file_at");
659    current_task.open_file_at(
660        dir_fd,
661        path.as_ref(),
662        OpenFlags::from_bits_truncate(flags),
663        mode,
664        resolve_flags,
665        AccessCheck::default(),
666    )
667}
668
669fn lookup_parent_at<T, F>(
670    current_task: &CurrentTask,
671    dir_fd: FdNumber,
672    user_path: UserCString,
673    callback: F,
674) -> Result<T, Errno>
675where
676    F: Fn(LookupContext, NamespaceNode, &FsStr) -> Result<T, Errno>,
677{
678    let path = current_task.read_path(user_path)?;
679    log_trace!(dir_fd:%, path:%; "lookup_parent_at");
680    if path.is_empty() {
681        return error!(ENOENT);
682    }
683    let mut context = LookupContext::default();
684    let (parent, basename) = current_task.lookup_parent_at(&mut context, dir_fd, path.as_ref())?;
685    callback(context, parent, basename)
686}
687
688/// Options for lookup_at.
689#[derive(Debug, Default, Copy, Clone)]
690pub struct LookupFlags {
691    /// Whether AT_EMPTY_PATH was supplied.
692    allow_empty_path: bool,
693
694    /// Used to implement AT_SYMLINK_NOFOLLOW.
695    symlink_mode: SymlinkMode,
696
697    /// Automount directories on the path.
698    // TODO(https://fxbug.dev/297370602): Support the `AT_NO_AUTOMOUNT` flag.
699    #[allow(dead_code)]
700    automount: bool,
701}
702
703impl LookupFlags {
704    pub fn no_follow() -> Self {
705        Self { symlink_mode: SymlinkMode::NoFollow, ..Default::default() }
706    }
707
708    fn from_bits(flags: u32, allowed_flags: u32) -> Result<Self, Errno> {
709        if flags & !allowed_flags != 0 {
710            return error!(EINVAL);
711        }
712        let follow_symlinks = if allowed_flags & AT_SYMLINK_FOLLOW != 0 {
713            flags & AT_SYMLINK_FOLLOW != 0
714        } else {
715            flags & AT_SYMLINK_NOFOLLOW == 0
716        };
717        let automount =
718            if allowed_flags & AT_NO_AUTOMOUNT != 0 { flags & AT_NO_AUTOMOUNT == 0 } else { false };
719        if automount {
720            track_stub!(TODO("https://fxbug.dev/297370602"), "LookupFlags::automount");
721        }
722        Ok(LookupFlags {
723            allow_empty_path: (flags & AT_EMPTY_PATH != 0)
724                || (flags & O_PATH != 0 && flags & O_NOFOLLOW != 0),
725            symlink_mode: if follow_symlinks { SymlinkMode::Follow } else { SymlinkMode::NoFollow },
726            automount,
727        })
728    }
729}
730
731impl From<StatxFlags> for LookupFlags {
732    fn from(flags: StatxFlags) -> Self {
733        let lookup_flags = StatxFlags::AT_SYMLINK_NOFOLLOW
734            | StatxFlags::AT_EMPTY_PATH
735            | StatxFlags::AT_NO_AUTOMOUNT;
736        Self::from_bits((flags & lookup_flags).bits(), lookup_flags.bits()).unwrap()
737    }
738}
739
740pub fn lookup_at(
741    current_task: &CurrentTask,
742    dir_fd: FdNumber,
743    user_path: UserCString,
744    options: LookupFlags,
745) -> Result<NamespaceNode, Errno> {
746    let path = current_task.read_path(user_path)?;
747    log_trace!(dir_fd:%, path:%; "lookup_at");
748    if path.is_empty() {
749        if options.allow_empty_path {
750            let (node, _) =
751                current_task.resolve_dir_fd(dir_fd, path.as_ref(), ResolveFlags::empty())?;
752            return Ok(node);
753        }
754        return error!(ENOENT);
755    }
756
757    let mut parent_context = LookupContext::default();
758    let (parent, basename) =
759        current_task.lookup_parent_at(&mut parent_context, dir_fd, path.as_ref())?;
760
761    let mut child_context = if parent_context.must_be_directory {
762        // The child must resolve to a directory. This is because a trailing slash
763        // was found in the path. If the child is a symlink, we should follow it.
764        // See https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap03.html#tag_21_03_00_75
765        parent_context.with(SymlinkMode::Follow)
766    } else {
767        parent_context.with(options.symlink_mode)
768    };
769
770    parent.lookup_child(current_task, &mut child_context, basename)
771}
772
773fn do_openat(
774    current_task: &CurrentTask,
775    dir_fd: FdNumber,
776    user_path: UserCString,
777    flags: u32,
778    mode: FileMode,
779    resolve_flags: ResolveFlags,
780) -> Result<FdNumber, Errno> {
781    let file = open_file_at(current_task, dir_fd, user_path, flags, mode, resolve_flags)?;
782    let fd_flags = get_fd_flags(flags);
783    current_task.add_file(file, fd_flags)
784}
785
786pub fn sys_openat(
787    current_task: &CurrentTask,
788    dir_fd: FdNumber,
789    user_path: UserCString,
790    flags: u32,
791    mode: FileMode,
792) -> Result<FdNumber, Errno> {
793    do_openat(current_task, dir_fd, user_path, flags, mode, ResolveFlags::empty())
794}
795
796pub fn sys_openat2(
797    current_task: &CurrentTask,
798    dir_fd: FdNumber,
799    user_path: UserCString,
800    how_ref: UserRef<uapi::open_how>,
801    size: usize,
802) -> Result<FdNumber, Errno> {
803    const EXPECTED_SIZE: usize = std::mem::size_of::<uapi::open_how>();
804    if size < EXPECTED_SIZE {
805        return error!(EINVAL);
806    }
807
808    let how = current_task.read_object(how_ref)?;
809
810    // If the `size` is greater than expected, then we need to check that any extra bytes after
811    // `open_how` are set to 0. This is needed to properly handle the case when `open_how` is
812    // extended with new fields in the future. There is no upper limit on the buffer size, so we
813    // limit size of each read to one page.
814    let mut pos = EXPECTED_SIZE;
815    while pos < size {
816        let length = std::cmp::min(size - pos, *PAGE_SIZE as usize);
817        let extra_bytes =
818            current_task.read_buffer(&UserBuffer { address: (how_ref.addr() + pos)?, length })?;
819        for b in extra_bytes {
820            if b != 0 {
821                return error!(E2BIG);
822            }
823        }
824        pos += length;
825    }
826
827    let flags: u32 = how.flags.try_into().map_err(|_| errno!(EINVAL))?;
828
829    // `mode` can be specified only with `O_CREAT` or `O_TMPFILE`.
830    let allowed_mode_flags = if (flags & (O_CREAT | O_TMPFILE)) > 0 { 0o7777 } else { 0 };
831    if (how.mode & !allowed_mode_flags) != 0 {
832        return error!(EINVAL);
833    }
834
835    let mode = FileMode::from_bits(how.mode.try_into().map_err(|_| errno!(EINVAL))?);
836    let resolve_flags =
837        ResolveFlags::from_bits(how.resolve.try_into().map_err(|_| errno!(EINVAL))?)
838            .ok_or_else(|| errno!(EINVAL))?;
839
840    if resolve_flags.contains(ResolveFlags::CACHED) {
841        track_stub!(TODO("https://fxbug.dev/326474574"), "openat2: RESOLVE_CACHED");
842        return error!(EAGAIN);
843    }
844
845    do_openat(current_task, dir_fd, user_path, flags, mode, resolve_flags)
846}
847
848pub fn sys_faccessat(
849    current_task: &CurrentTask,
850    dir_fd: FdNumber,
851    user_path: UserCString,
852    mode: u32,
853) -> Result<(), Errno> {
854    sys_faccessat2(current_task, dir_fd, user_path, mode, 0)
855}
856
857pub fn sys_faccessat2(
858    current_task: &CurrentTask,
859    dir_fd: FdNumber,
860    user_path: UserCString,
861    mode: u32,
862    flags: u32,
863) -> Result<(), Errno> {
864    let access_check = || {
865        let mode = Access::try_from(mode)?;
866        let lookup_flags = LookupFlags::from_bits(flags, AT_SYMLINK_NOFOLLOW | AT_EACCESS)?;
867        let name = lookup_at(current_task, dir_fd, user_path, lookup_flags)?;
868        name.check_access(current_task, mode, CheckAccessReason::Access)
869    };
870    // Unless `AT_ACCESS` is set, perform lookup & access-checking using real UID & GID.
871    if flags & AT_EACCESS == 0 {
872        let mut temporary_creds = Credentials::clone(&current_task.current_creds());
873        temporary_creds.fsuid = temporary_creds.uid;
874        temporary_creds.fsgid = temporary_creds.gid;
875
876        // access() for root users should use permitted capabilities instead of effective capabilities.
877        // access() for non-root users should use an empty set of capabilities.
878        if temporary_creds.uid == 0 {
879            temporary_creds.cap_effective = temporary_creds.cap_permitted;
880        } else {
881            temporary_creds.cap_effective = Capabilities::empty();
882        }
883
884        current_task.override_creds(temporary_creds.into(), access_check)
885    } else {
886        access_check()
887    }
888}
889
890pub fn sys_getdents64(
891    current_task: &CurrentTask,
892    fd: FdNumber,
893    user_buffer: UserAddress,
894    user_capacity: usize,
895) -> Result<usize, Errno> {
896    let file = current_task.files().get(fd)?;
897    let mut offset = file.offset.copy();
898    let mut sink = DirentSink64::new(current_task, &mut *offset, user_buffer, user_capacity);
899    let result = file.readdir(current_task, &mut sink);
900    let ret = sink.map_result_with_actual(result);
901    offset.update();
902    ret
903}
904
905pub fn sys_chroot(current_task: &CurrentTask, user_path: UserCString) -> Result<(), Errno> {
906    let name = lookup_at(current_task, FdNumber::AT_FDCWD, user_path, LookupFlags::default())?;
907    if !name.entry.node.is_dir() {
908        return error!(ENOTDIR);
909    }
910
911    current_task.fs().chroot(current_task, name)?;
912    Ok(())
913}
914
915pub fn sys_chdir(current_task: &CurrentTask, user_path: UserCString) -> Result<(), Errno> {
916    let name = lookup_at(current_task, FdNumber::AT_FDCWD, user_path, LookupFlags::default())?;
917    if !name.entry.node.is_dir() {
918        return error!(ENOTDIR);
919    }
920    current_task.fs().chdir(current_task, name)
921}
922
923pub fn sys_fchdir(current_task: &CurrentTask, fd: FdNumber) -> Result<(), Errno> {
924    // O_PATH allowed for:
925    //
926    //   fchdir(2), if the file descriptor refers to a directory
927    //   (since Linux 3.5).
928    //
929    // See https://man7.org/linux/man-pages/man2/open.2.html
930    let file = current_task.files().get_allowing_opath(fd)?;
931    if !file.name.entry.node.is_dir() {
932        return error!(ENOTDIR);
933    }
934    current_task.fs().chdir(current_task, file.name.to_passive())
935}
936
937pub fn sys_fstat(
938    current_task: &CurrentTask,
939    fd: FdNumber,
940    buffer: UserRef<uapi::stat>,
941) -> Result<(), Errno> {
942    // O_PATH allowed for:
943    //
944    //   fstat(2) (since Linux 3.6).
945    //
946    // See https://man7.org/linux/man-pages/man2/open.2.html
947    let file = current_task.files().get_allowing_opath(fd)?;
948    let result = file.node().stat(current_task)?;
949    current_task.write_object(buffer, &result)?;
950    Ok(())
951}
952
953type StatPtr = MultiArchUserRef<uapi::stat, uapi::arch32::stat64>;
954
955// TODO(https://fxbug.dev/485370648) remove when unnecessary
956fn get_fake_ion_stat() -> uapi::stat {
957    uapi::stat {
958        st_mode: uapi::S_IFCHR | 0o666,
959        st_rdev: DeviceId::new(10, 59).bits(),
960        st_nlink: 1,
961        st_blksize: 4096,
962        ..Default::default()
963    }
964}
965
966// TODO(https://fxbug.dev/485370648) remove when unnecessary
967fn get_fake_ion_statx() -> statx {
968    statx {
969        stx_mask: uapi::STATX_BASIC_STATS,
970        stx_mode: (uapi::S_IFCHR | 0o666) as u16,
971        stx_rdev_major: 10,
972        stx_rdev_minor: 59,
973        stx_nlink: 1,
974        stx_blksize: 4096,
975        ..Default::default()
976    }
977}
978
979pub fn sys_fstatat64(
980    current_task: &CurrentTask,
981    dir_fd: FdNumber,
982    user_path: UserCString,
983    buffer: StatPtr,
984    flags: u32,
985) -> Result<(), Errno> {
986    let lookup_flags =
987        LookupFlags::from_bits(flags, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT)?;
988    let result = match lookup_at(current_task, dir_fd, user_path, lookup_flags) {
989        Ok(name) => name.entry.node.stat(current_task)?,
990        // TODO(https://fxbug.dev/485370648) remove when unnecessary
991        Err(e) if e == errno!(ENOENT) && current_task.kernel().features.fake_ion => {
992            let path = current_task.read_path(user_path)?;
993            if path == b"/dev/ion" {
994                get_fake_ion_stat()
995            } else {
996                return Err(e);
997            }
998        }
999        Err(e) => return Err(e),
1000    };
1001    current_task.write_multi_arch_object(buffer, result)?;
1002    Ok(())
1003}
1004
1005pub use sys_fstatat64 as sys_newfstatat;
1006
1007pub fn sys_statx(
1008    current_task: &CurrentTask,
1009    dir_fd: FdNumber,
1010    user_path: UserCString,
1011    flags: u32,
1012    mask: u32,
1013    statxbuf: UserRef<statx>,
1014) -> Result<(), Errno> {
1015    let statx_flags = StatxFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL))?;
1016    if statx_flags & (StatxFlags::AT_STATX_FORCE_SYNC | StatxFlags::AT_STATX_DONT_SYNC)
1017        == (StatxFlags::AT_STATX_FORCE_SYNC | StatxFlags::AT_STATX_DONT_SYNC)
1018    {
1019        return error!(EINVAL);
1020    }
1021
1022    let result = match lookup_at(current_task, dir_fd, user_path, LookupFlags::from(statx_flags)) {
1023        Ok(name) => name.entry.node.statx(current_task, statx_flags, mask)?,
1024        // TODO(https://fxbug.dev/485370648) remove when unnecessary
1025        Err(e) if e == errno!(ENOENT) && current_task.kernel().features.fake_ion => {
1026            let path = current_task.read_path(user_path)?;
1027            if path == b"/dev/ion" {
1028                get_fake_ion_statx()
1029            } else {
1030                return Err(e);
1031            }
1032        }
1033        Err(e) => return Err(e),
1034    };
1035    current_task.write_object(statxbuf, &result)?;
1036    Ok(())
1037}
1038
1039pub fn sys_readlinkat(
1040    current_task: &CurrentTask,
1041    dir_fd: FdNumber,
1042    user_path: UserCString,
1043    buffer: UserAddress,
1044    buffer_size: usize,
1045) -> Result<usize, Errno> {
1046    let path = current_task.read_path(user_path)?;
1047    let lookup_flags = if path.is_empty() {
1048        if dir_fd == FdNumber::AT_FDCWD {
1049            return error!(ENOENT);
1050        }
1051        LookupFlags {
1052            allow_empty_path: true,
1053            symlink_mode: SymlinkMode::NoFollow,
1054            ..Default::default()
1055        }
1056    } else {
1057        LookupFlags::no_follow()
1058    };
1059    let name = lookup_at(current_task, dir_fd, user_path, lookup_flags)?;
1060
1061    let target = match name.readlink(current_task)? {
1062        SymlinkTarget::Path(path) => path,
1063        SymlinkTarget::Node(node) => node.path(&current_task.fs()),
1064    };
1065
1066    if buffer_size == 0 {
1067        return error!(EINVAL);
1068    }
1069    // Cap the returned length at buffer_size.
1070    let length = std::cmp::min(buffer_size, target.len());
1071    current_task.write_memory(buffer, &target[..length])?;
1072    Ok(length)
1073}
1074
1075pub fn sys_truncate(
1076    current_task: &CurrentTask,
1077    user_path: UserCString,
1078    length: off_t,
1079) -> Result<(), Errno> {
1080    let length = length.try_into().map_err(|_| errno!(EINVAL))?;
1081    let name = lookup_at(current_task, FdNumber::AT_FDCWD, user_path, LookupFlags::default())?;
1082    name.truncate(current_task, length)?;
1083    Ok(())
1084}
1085
1086pub fn sys_ftruncate(current_task: &CurrentTask, fd: FdNumber, length: off_t) -> Result<(), Errno> {
1087    let length = length.try_into().map_err(|_| errno!(EINVAL))?;
1088    let file = current_task.files().get(fd)?;
1089    file.ftruncate(current_task, length)?;
1090    Ok(())
1091}
1092
1093pub fn sys_mkdirat(
1094    current_task: &CurrentTask,
1095    dir_fd: FdNumber,
1096    user_path: UserCString,
1097    mode: FileMode,
1098) -> Result<(), Errno> {
1099    let path = current_task.read_path(user_path)?;
1100
1101    if path.is_empty() {
1102        return error!(ENOENT);
1103    }
1104    let (parent, basename) =
1105        current_task.lookup_parent_at(&mut LookupContext::default(), dir_fd, path.as_ref())?;
1106    parent.create_node(current_task, basename, mode.with_type(FileMode::IFDIR), DeviceId::NONE)?;
1107    Ok(())
1108}
1109
1110pub fn sys_mknodat(
1111    current_task: &CurrentTask,
1112    dir_fd: FdNumber,
1113    user_path: UserCString,
1114    mode: FileMode,
1115    dev: DeviceId,
1116) -> Result<(), Errno> {
1117    let file_type = match mode.fmt() {
1118        FileMode::IFREG
1119        | FileMode::IFCHR
1120        | FileMode::IFBLK
1121        | FileMode::IFIFO
1122        | FileMode::IFSOCK => mode.fmt(),
1123        FileMode::EMPTY => FileMode::IFREG,
1124        _ => return error!(EINVAL),
1125    };
1126    lookup_parent_at(current_task, dir_fd, user_path, |_, parent, basename| {
1127        parent.create_node(current_task, basename, mode.with_type(file_type), dev)
1128    })?;
1129    Ok(())
1130}
1131
1132pub fn sys_linkat(
1133    current_task: &CurrentTask,
1134    old_dir_fd: FdNumber,
1135    old_user_path: UserCString,
1136    new_dir_fd: FdNumber,
1137    new_user_path: UserCString,
1138    flags: u32,
1139) -> Result<(), Errno> {
1140    if flags & !(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH) != 0 {
1141        track_stub!(TODO("https://fxbug.dev/322875706"), "linkat unknown flags", flags);
1142        return error!(EINVAL);
1143    }
1144
1145    if flags & AT_EMPTY_PATH != 0 {
1146        security::check_task_capable(current_task, CAP_DAC_READ_SEARCH)
1147            .map_err(|_| errno!(ENOENT))?;
1148    }
1149
1150    let flags = LookupFlags::from_bits(flags, AT_EMPTY_PATH | AT_SYMLINK_FOLLOW)?;
1151    let target = lookup_at(current_task, old_dir_fd, old_user_path, flags)?;
1152    lookup_parent_at(current_task, new_dir_fd, new_user_path, |context, parent, basename| {
1153        // The path to a new link cannot end in `/`. That would imply that we are dereferencing
1154        // the link to a directory.
1155        if context.must_be_directory {
1156            return error!(ENOENT);
1157        }
1158        if target.mount != parent.mount {
1159            return error!(EXDEV);
1160        }
1161        parent.link(current_task, basename, &target.entry.node)
1162    })?;
1163
1164    Ok(())
1165}
1166
1167pub fn sys_unlinkat(
1168    current_task: &CurrentTask,
1169    dir_fd: FdNumber,
1170    user_path: UserCString,
1171    flags: u32,
1172) -> Result<(), Errno> {
1173    if flags & !AT_REMOVEDIR != 0 {
1174        return error!(EINVAL);
1175    }
1176    let kind =
1177        if flags & AT_REMOVEDIR != 0 { UnlinkKind::Directory } else { UnlinkKind::NonDirectory };
1178    lookup_parent_at(current_task, dir_fd, user_path, |context, parent, basename| {
1179        parent.unlink(current_task, basename, kind, context.must_be_directory)
1180    })?;
1181    Ok(())
1182}
1183
1184pub fn sys_renameat2(
1185    current_task: &CurrentTask,
1186    old_dir_fd: FdNumber,
1187    old_user_path: UserCString,
1188    new_dir_fd: FdNumber,
1189    new_user_path: UserCString,
1190    flags: u32,
1191) -> Result<(), Errno> {
1192    let flags = RenameFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL))?;
1193    if flags.intersects(RenameFlags::INTERNAL) {
1194        return error!(EINVAL);
1195    };
1196
1197    // RENAME_EXCHANGE cannot be combined with the other flags.
1198    if flags.contains(RenameFlags::EXCHANGE)
1199        && flags.intersects(RenameFlags::NOREPLACE | RenameFlags::WHITEOUT)
1200    {
1201        return error!(EINVAL);
1202    }
1203
1204    // RENAME_WHITEOUT is not supported.
1205    if flags.contains(RenameFlags::WHITEOUT) {
1206        track_stub!(TODO("https://fxbug.dev/322875416"), "RENAME_WHITEOUT");
1207        return error!(ENOSYS);
1208    };
1209
1210    let lookup = |dir_fd, user_path| {
1211        lookup_parent_at(current_task, dir_fd, user_path, |_, parent, basename| {
1212            Ok((parent, basename.to_owned()))
1213        })
1214    };
1215
1216    let (old_parent, old_basename) = lookup(old_dir_fd, old_user_path)?;
1217    let (new_parent, new_basename) = lookup(new_dir_fd, new_user_path)?;
1218
1219    if new_basename.len() > NAME_MAX as usize {
1220        return error!(ENAMETOOLONG);
1221    }
1222
1223    NamespaceNode::rename(
1224        current_task,
1225        &old_parent,
1226        old_basename.as_ref(),
1227        &new_parent,
1228        new_basename.as_ref(),
1229        flags,
1230    )
1231}
1232
1233pub fn sys_fchmod(current_task: &CurrentTask, fd: FdNumber, mode: FileMode) -> Result<(), Errno> {
1234    // Remove the filetype from the mode.
1235    let mode = mode & FileMode::PERMISSIONS;
1236    let file = current_task.files().get(fd)?;
1237    file.name.entry.node.chmod(current_task, &file.name.mount, mode)?;
1238    file.name.entry.notify_ignoring_excl_unlink(InotifyMask::ATTRIB);
1239    Ok(())
1240}
1241
1242pub fn sys_fchmodat(
1243    current_task: &CurrentTask,
1244    dir_fd: FdNumber,
1245    user_path: UserCString,
1246    mode: FileMode,
1247) -> Result<(), Errno> {
1248    // Remove the filetype from the mode.
1249    let mode = mode & FileMode::PERMISSIONS;
1250    let name = lookup_at(current_task, dir_fd, user_path, LookupFlags::default())?;
1251    name.entry.node.chmod(current_task, &name.mount, mode)?;
1252    name.entry.notify_ignoring_excl_unlink(InotifyMask::ATTRIB);
1253    Ok(())
1254}
1255
1256fn maybe_uid(id: u32) -> Option<uid_t> {
1257    if id == u32::MAX { None } else { Some(id) }
1258}
1259
1260pub fn sys_fchown(
1261    current_task: &CurrentTask,
1262    fd: FdNumber,
1263    owner: u32,
1264    group: u32,
1265) -> Result<(), Errno> {
1266    let file = current_task.files().get(fd)?;
1267    file.name.entry.node.chown(
1268        current_task,
1269        &file.name.mount,
1270        maybe_uid(owner),
1271        maybe_uid(group),
1272    )?;
1273    file.name.entry.notify_ignoring_excl_unlink(InotifyMask::ATTRIB);
1274    Ok(())
1275}
1276
1277pub fn sys_fchownat(
1278    current_task: &CurrentTask,
1279    dir_fd: FdNumber,
1280    user_path: UserCString,
1281    owner: u32,
1282    group: u32,
1283    flags: u32,
1284) -> Result<(), Errno> {
1285    let flags = LookupFlags::from_bits(flags, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW)?;
1286    let name = lookup_at(current_task, dir_fd, user_path, flags)?;
1287    name.entry.node.chown(current_task, &name.mount, maybe_uid(owner), maybe_uid(group))?;
1288    name.entry.notify_ignoring_excl_unlink(InotifyMask::ATTRIB);
1289    Ok(())
1290}
1291
1292fn read_xattr_name(current_task: &CurrentTask, name_addr: UserCString) -> Result<FsString, Errno> {
1293    let name = current_task
1294        .read_c_string_to_vec(name_addr, XATTR_NAME_MAX as usize + 1)
1295        .map_err(|e| if e == ENAMETOOLONG { errno!(ERANGE) } else { e })?;
1296    if name.is_empty() {
1297        return error!(ERANGE);
1298    }
1299    let dot_index = memchr::memchr(b'.', &name).ok_or_else(|| errno!(ENOTSUP))?;
1300    if name[dot_index + 1..].is_empty() {
1301        return error!(EINVAL);
1302    }
1303    match &name[..dot_index] {
1304        b"user" | b"security" | b"trusted" | b"system" => {}
1305        _ => return error!(ENOTSUP),
1306    }
1307    Ok(name)
1308}
1309
1310fn do_getxattr(
1311    current_task: &CurrentTask,
1312    node: &NamespaceNode,
1313    name_addr: UserCString,
1314    value_addr: UserAddress,
1315    size: usize,
1316) -> Result<usize, Errno> {
1317    let name = read_xattr_name(current_task, name_addr)?;
1318    let value = match node.entry.node.get_xattr(current_task, &node.mount, name.as_ref(), size)? {
1319        ValueOrSize::Size(s) => return Ok(s),
1320        ValueOrSize::Value(v) => v,
1321    };
1322    if size == 0 {
1323        return Ok(value.len());
1324    }
1325    if size < value.len() {
1326        return error!(ERANGE);
1327    }
1328    current_task.write_memory(value_addr, &value)
1329}
1330
1331pub fn sys_getxattr(
1332    current_task: &CurrentTask,
1333    path_addr: UserCString,
1334    name_addr: UserCString,
1335    value_addr: UserAddress,
1336    size: usize,
1337) -> Result<usize, Errno> {
1338    let node = lookup_at(current_task, FdNumber::AT_FDCWD, path_addr, LookupFlags::default())?;
1339    do_getxattr(current_task, &node, name_addr, value_addr, size)
1340}
1341
1342pub fn sys_fgetxattr(
1343    current_task: &CurrentTask,
1344    fd: FdNumber,
1345    name_addr: UserCString,
1346    value_addr: UserAddress,
1347    size: usize,
1348) -> Result<usize, Errno> {
1349    let file = current_task.files().get(fd)?;
1350    do_getxattr(current_task, &file.name, name_addr, value_addr, size)
1351}
1352
1353pub fn sys_lgetxattr(
1354    current_task: &CurrentTask,
1355    path_addr: UserCString,
1356    name_addr: UserCString,
1357    value_addr: UserAddress,
1358    size: usize,
1359) -> Result<usize, Errno> {
1360    let node = lookup_at(current_task, FdNumber::AT_FDCWD, path_addr, LookupFlags::no_follow())?;
1361    do_getxattr(current_task, &node, name_addr, value_addr, size)
1362}
1363
1364fn do_setxattr(
1365    current_task: &CurrentTask,
1366    node: &NamespaceNode,
1367    name_addr: UserCString,
1368    value_addr: UserAddress,
1369    size: usize,
1370    flags: u32,
1371) -> Result<(), Errno> {
1372    if size > XATTR_NAME_MAX as usize {
1373        return error!(E2BIG);
1374    }
1375
1376    let op = match flags {
1377        0 => XattrOp::Set,
1378        XATTR_CREATE => XattrOp::Create,
1379        XATTR_REPLACE => XattrOp::Replace,
1380        _ => return error!(EINVAL),
1381    };
1382    let name = read_xattr_name(current_task, name_addr)?;
1383    let value = FsString::from(current_task.read_memory_to_vec(value_addr, size)?);
1384    node.entry.node.set_xattr(current_task, &node.mount, name.as_ref(), value.as_ref(), op)
1385}
1386
1387pub fn sys_fsetxattr(
1388    current_task: &CurrentTask,
1389    fd: FdNumber,
1390    name_addr: UserCString,
1391    value_addr: UserAddress,
1392    size: usize,
1393    flags: u32,
1394) -> Result<(), Errno> {
1395    let file = current_task.files().get(fd)?;
1396    do_setxattr(current_task, &file.name, name_addr, value_addr, size, flags)
1397}
1398
1399pub fn sys_lsetxattr(
1400    current_task: &CurrentTask,
1401    path_addr: UserCString,
1402    name_addr: UserCString,
1403    value_addr: UserAddress,
1404    size: usize,
1405    flags: u32,
1406) -> Result<(), Errno> {
1407    let node = lookup_at(current_task, FdNumber::AT_FDCWD, path_addr, LookupFlags::no_follow())?;
1408    do_setxattr(current_task, &node, name_addr, value_addr, size, flags)
1409}
1410
1411pub fn sys_setxattr(
1412    current_task: &CurrentTask,
1413    path_addr: UserCString,
1414    name_addr: UserCString,
1415    value_addr: UserAddress,
1416    size: usize,
1417    flags: u32,
1418) -> Result<(), Errno> {
1419    let node = lookup_at(current_task, FdNumber::AT_FDCWD, path_addr, LookupFlags::default())?;
1420    do_setxattr(current_task, &node, name_addr, value_addr, size, flags)
1421}
1422
1423fn do_removexattr(
1424    current_task: &CurrentTask,
1425    node: &NamespaceNode,
1426    name_addr: UserCString,
1427) -> Result<(), Errno> {
1428    let mode = node.entry.node.info().mode;
1429    if mode.is_chr() || mode.is_fifo() {
1430        return error!(EPERM);
1431    }
1432    let name = read_xattr_name(current_task, name_addr)?;
1433    node.entry.node.remove_xattr(current_task, &node.mount, name.as_ref())
1434}
1435
1436pub fn sys_removexattr(
1437    current_task: &CurrentTask,
1438    path_addr: UserCString,
1439    name_addr: UserCString,
1440) -> Result<(), Errno> {
1441    let node = lookup_at(current_task, FdNumber::AT_FDCWD, path_addr, LookupFlags::default())?;
1442    do_removexattr(current_task, &node, name_addr)
1443}
1444
1445pub fn sys_lremovexattr(
1446    current_task: &CurrentTask,
1447    path_addr: UserCString,
1448    name_addr: UserCString,
1449) -> Result<(), Errno> {
1450    let node = lookup_at(current_task, FdNumber::AT_FDCWD, path_addr, LookupFlags::no_follow())?;
1451    do_removexattr(current_task, &node, name_addr)
1452}
1453
1454pub fn sys_fremovexattr(
1455    current_task: &CurrentTask,
1456    fd: FdNumber,
1457    name_addr: UserCString,
1458) -> Result<(), Errno> {
1459    let file = current_task.files().get(fd)?;
1460    do_removexattr(current_task, &file.name, name_addr)
1461}
1462
1463fn do_listxattr(
1464    current_task: &CurrentTask,
1465    node: &NamespaceNode,
1466    list_addr: UserAddress,
1467    size: usize,
1468) -> Result<usize, Errno> {
1469    let security_xattr = security::fs_node_listsecurity(current_task, &node.entry.node);
1470    let xattrs = match node.entry.node.list_xattrs(current_task, size) {
1471        Ok(ValueOrSize::Size(s)) => return Ok(s + security_xattr.map_or(0, |s| s.len() + 1)),
1472        Ok(ValueOrSize::Value(mut v)) => {
1473            if let Some(security_value) = security_xattr {
1474                if !v.contains(&security_value) {
1475                    v.push(security_value);
1476                }
1477            }
1478            v
1479        }
1480        Err(e) => {
1481            if e.code != ENOTSUP || security_xattr.is_none() {
1482                return Err(e);
1483            }
1484            vec![security_xattr.unwrap()]
1485        }
1486    };
1487
1488    let mut list = vec![];
1489    for name in xattrs.iter() {
1490        list.extend_from_slice(name);
1491        list.push(b'\0');
1492    }
1493    if size == 0 {
1494        return Ok(list.len());
1495    }
1496    if size < list.len() {
1497        return error!(ERANGE);
1498    }
1499    current_task.write_memory(list_addr, &list)
1500}
1501
1502pub fn sys_listxattr(
1503    current_task: &CurrentTask,
1504    path_addr: UserCString,
1505    list_addr: UserAddress,
1506    size: usize,
1507) -> Result<usize, Errno> {
1508    let node = lookup_at(current_task, FdNumber::AT_FDCWD, path_addr, LookupFlags::default())?;
1509    do_listxattr(current_task, &node, list_addr, size)
1510}
1511
1512pub fn sys_llistxattr(
1513    current_task: &CurrentTask,
1514    path_addr: UserCString,
1515    list_addr: UserAddress,
1516    size: usize,
1517) -> Result<usize, Errno> {
1518    let node = lookup_at(current_task, FdNumber::AT_FDCWD, path_addr, LookupFlags::no_follow())?;
1519    do_listxattr(current_task, &node, list_addr, size)
1520}
1521
1522pub fn sys_flistxattr(
1523    current_task: &CurrentTask,
1524    fd: FdNumber,
1525    list_addr: UserAddress,
1526    size: usize,
1527) -> Result<usize, Errno> {
1528    let file = current_task.files().get(fd)?;
1529    do_listxattr(current_task, &file.name, list_addr, size)
1530}
1531
1532pub fn sys_getcwd(
1533    current_task: &CurrentTask,
1534    buf: UserAddress,
1535    size: usize,
1536) -> Result<usize, Errno> {
1537    let root = current_task.fs().root();
1538    let cwd = current_task.fs().cwd();
1539    let mut user_cwd = match cwd.path_from_root(Some(&root)) {
1540        PathWithReachability::Reachable(path) => path,
1541        PathWithReachability::Unreachable(mut path) => {
1542            let mut combined = vec![];
1543            combined.extend_from_slice(b"(unreachable)");
1544            combined.append(&mut path);
1545            combined.into()
1546        }
1547    };
1548    user_cwd.push(b'\0');
1549    if user_cwd.len() > size {
1550        return error!(ERANGE);
1551    }
1552    current_task.write_memory(buf, &user_cwd)?;
1553    Ok(user_cwd.len())
1554}
1555
1556pub fn sys_umask(current_task: &CurrentTask, umask: FileMode) -> Result<FileMode, Errno> {
1557    Ok(current_task.fs().set_umask(umask))
1558}
1559
1560fn get_fd_flags(flags: u32) -> FdFlags {
1561    if flags & O_CLOEXEC != 0 { FdFlags::CLOEXEC } else { FdFlags::empty() }
1562}
1563
1564pub fn sys_pipe2(
1565    current_task: &CurrentTask,
1566    user_pipe: UserRef<FdNumber>,
1567    flags: u32,
1568) -> Result<(), Errno> {
1569    let supported_file_flags = OpenFlags::NONBLOCK | OpenFlags::DIRECT;
1570    if flags & !(O_CLOEXEC | supported_file_flags.bits()) != 0 {
1571        return error!(EINVAL);
1572    }
1573    let (read, write) = new_pipe(current_task)?;
1574
1575    let file_flags = OpenFlags::from_bits_truncate(flags & supported_file_flags.bits());
1576    read.update_file_flags(file_flags, supported_file_flags);
1577    write.update_file_flags(file_flags, supported_file_flags);
1578
1579    let fd_flags = get_fd_flags(flags);
1580    let fd_read = current_task.add_file(read, fd_flags)?;
1581    let fd_write = current_task.add_file(write, fd_flags)?;
1582    log_trace!("pipe2 -> [{:#x}, {:#x}]", fd_read.raw(), fd_write.raw());
1583
1584    current_task.write_object(user_pipe, &fd_read)?;
1585    let user_pipe = user_pipe.next()?;
1586    current_task.write_object(user_pipe, &fd_write)?;
1587
1588    Ok(())
1589}
1590
1591pub fn sys_ioctl(
1592    current_task: &CurrentTask,
1593    fd: FdNumber,
1594    request: u32,
1595    arg: SyscallArg,
1596) -> Result<SyscallResult, Errno> {
1597    match request {
1598        FIOCLEX | FIONCLEX => {
1599            current_task.files().ioctl_fd_flags(current_task, fd, request)?;
1600            Ok(SUCCESS)
1601        }
1602        _ => {
1603            let file = current_task.files().get(fd)?;
1604            file.ioctl(current_task, request, arg)
1605        }
1606    }
1607}
1608
1609pub fn sys_symlinkat(
1610    current_task: &CurrentTask,
1611    user_target: UserCString,
1612    new_dir_fd: FdNumber,
1613    user_path: UserCString,
1614) -> Result<(), Errno> {
1615    let target = current_task.read_path(user_target)?;
1616    if target.is_empty() {
1617        return error!(ENOENT);
1618    }
1619
1620    let path = current_task.read_path(user_path)?;
1621    // TODO: This check could probably be moved into parent.symlink(..).
1622    if path.is_empty() {
1623        return error!(ENOENT);
1624    }
1625
1626    let res = lookup_parent_at(current_task, new_dir_fd, user_path, |context, parent, basename| {
1627        // The path to a new symlink cannot end in `/`. That would imply that we are dereferencing
1628        // the symlink to a directory.
1629        //
1630        // See https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap03.html#tag_21_03_00_75
1631        if context.must_be_directory {
1632            return error!(ENOENT);
1633        }
1634        parent.create_symlink(current_task, basename, target.as_ref())
1635    });
1636    res?;
1637    Ok(())
1638}
1639
1640pub fn sys_dup(current_task: &CurrentTask, oldfd: FdNumber) -> Result<FdNumber, Errno> {
1641    current_task.files().duplicate(current_task, oldfd, TargetFdNumber::Default, FdFlags::empty())
1642}
1643
1644pub fn sys_dup3(
1645    current_task: &CurrentTask,
1646    oldfd: FdNumber,
1647    newfd: FdNumber,
1648    flags: u32,
1649) -> Result<FdNumber, Errno> {
1650    if oldfd == newfd {
1651        return error!(EINVAL);
1652    }
1653    if flags & !O_CLOEXEC != 0 {
1654        return error!(EINVAL);
1655    }
1656    let fd_flags = get_fd_flags(flags);
1657    current_task.files().duplicate(
1658        current_task,
1659        oldfd,
1660        TargetFdNumber::Specific(newfd),
1661        fd_flags,
1662    )?;
1663    Ok(newfd)
1664}
1665
1666/// A memfd file descriptor cannot have a name longer than 250 bytes, including
1667/// the null terminator.
1668///
1669/// See Errors section of https://man7.org/linux/man-pages/man2/memfd_create.2.html
1670const MEMFD_NAME_MAX_LEN: usize = 250;
1671
1672pub fn sys_memfd_create(
1673    current_task: &CurrentTask,
1674    user_name: UserCString,
1675    flags: u32,
1676) -> Result<FdNumber, Errno> {
1677    const HUGE_SHIFTED_MASK: u32 = MFD_HUGE_MASK << MFD_HUGE_SHIFT;
1678
1679    if flags
1680        & !(MFD_CLOEXEC
1681            | MFD_ALLOW_SEALING
1682            | MFD_HUGETLB
1683            | HUGE_SHIFTED_MASK
1684            | MFD_NOEXEC_SEAL
1685            | MFD_EXEC)
1686        != 0
1687    {
1688        track_stub!(TODO("https://fxbug.dev/322875665"), "memfd_create unknown flags", flags);
1689        return error!(EINVAL);
1690    }
1691
1692    let _huge_page_size = if flags & MFD_HUGETLB != 0 {
1693        Some(flags & HUGE_SHIFTED_MASK)
1694    } else {
1695        if flags & HUGE_SHIFTED_MASK != 0 {
1696            return error!(EINVAL);
1697        }
1698        None
1699    };
1700
1701    let name = current_task
1702        .read_c_string_to_vec(user_name, MEMFD_NAME_MAX_LEN)
1703        .map_err(|e| if e == ENAMETOOLONG { errno!(EINVAL) } else { e })?;
1704
1705    // This behavior matches MEMFD_NOEXEC_SCOPE_EXEC, which states:
1706    //   > memfd_create() without MFD_EXEC nor MFD_NOEXEC_SEAL acts like MFD_EXEC was set.
1707    //
1708    // This behavior can be changed on Linux via sysctl vm.memfd_noexec, which is pid namespaced.
1709    // We do not currently support changing this behavior.
1710    let seals = if flags & MFD_NOEXEC_SEAL != 0 {
1711        SealFlags::NO_EXEC
1712    } else if flags & MFD_ALLOW_SEALING != 0 {
1713        SealFlags::empty()
1714    } else {
1715        // Forbid sealing, by sealing the seal operation.
1716        SealFlags::SEAL
1717    };
1718
1719    let file = new_memfd(current_task, name, seals, OpenFlags::RDWR)?;
1720
1721    let mut fd_flags = FdFlags::empty();
1722    if flags & MFD_CLOEXEC != 0 {
1723        fd_flags |= FdFlags::CLOEXEC;
1724    }
1725    let fd = current_task.add_file(file, fd_flags)?;
1726    Ok(fd)
1727}
1728
1729pub fn sys_mount(
1730    current_task: &CurrentTask,
1731    source_addr: UserCString,
1732    target_addr: UserCString,
1733    filesystemtype_addr: UserCString,
1734    flags: u32,
1735    data_addr: UserCString,
1736) -> Result<(), Errno> {
1737    security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1738
1739    let flags = MountFlags::from_bits(flags).ok_or_else(|| {
1740        track_stub!(
1741            TODO("https://fxbug.dev/322875327"),
1742            "mount unknown flags",
1743            flags & !MountFlags::from_bits_truncate(flags).bits()
1744        );
1745        errno!(EINVAL)
1746    })?;
1747
1748    let target = lookup_at(current_task, FdNumber::AT_FDCWD, target_addr, LookupFlags::default())?;
1749
1750    security::sb_mount(current_task, &target, flags)?;
1751
1752    if flags.contains(MountFlags::REMOUNT) {
1753        do_mount_remount(current_task, target, flags, data_addr)
1754    } else if flags.contains(MountFlags::BIND) {
1755        do_mount_bind(current_task, source_addr, target, flags)
1756    } else if flags.intersects(MountFlags::SHARED | MountFlags::PRIVATE | MountFlags::DOWNSTREAM) {
1757        do_mount_change_propagation_type(current_task, target, flags)
1758    } else if flags.contains(MountFlags::MOVE) {
1759        do_mount_move(current_task, source_addr, target)
1760    } else {
1761        do_mount_create(current_task, source_addr, target, filesystemtype_addr, data_addr, flags)
1762    }
1763}
1764
1765fn do_mount_remount(
1766    current_task: &CurrentTask,
1767    target: NamespaceNode,
1768    flags: MountFlags,
1769    data_addr: UserCString,
1770) -> Result<(), Errno> {
1771    if !data_addr.is_null() {
1772        track_stub!(TODO("https://fxbug.dev/322875506"), "MS_REMOUNT: Updating data");
1773    }
1774    let mount = target.mount_if_root()?;
1775
1776    let data = current_task.read_path_if_non_null(data_addr)?;
1777    let mount_options =
1778        security::sb_eat_lsm_opts(current_task.kernel(), &mut MountParams::parse(data.as_ref())?)?;
1779
1780    // From <https://man7.org/linux/man-pages/man2/mount.2.html>
1781    //
1782    //   Since Linux 2.6.26, the MS_REMOUNT flag can be used with MS_BIND
1783    //   to modify only the per-mount-point flags.  This is particularly
1784    //   useful for setting or clearing the "read-only" flag on a mount
1785    //   without changing the underlying filesystem.
1786    if !flags.contains(MountFlags::BIND) {
1787        security::sb_remount(current_task, &mount, mount_options)?;
1788        mount.reconfigure_fs(current_task, flags.file_system_flags())?;
1789    }
1790
1791    let updated_flags = flags & MountFlags::CHANGEABLE_WITH_REMOUNT;
1792    mount.update_flags(updated_flags.mountpoint_flags());
1793
1794    Ok(())
1795}
1796
1797fn do_mount_bind(
1798    current_task: &CurrentTask,
1799    source_addr: UserCString,
1800    target: NamespaceNode,
1801    flags: MountFlags,
1802) -> Result<(), Errno> {
1803    let source = lookup_at(current_task, FdNumber::AT_FDCWD, source_addr, LookupFlags::default())?;
1804    log_trace!(
1805        source:% = source.path(&current_task.fs()),
1806        target:% = target.path(&current_task.fs()),
1807        flags:?;
1808        "do_mount_bind",
1809    );
1810    target.mount(WhatToMount::Bind(source), flags.mountpoint_flags())
1811}
1812
1813fn do_mount_change_propagation_type(
1814    current_task: &CurrentTask,
1815    target: NamespaceNode,
1816    flags: MountFlags,
1817) -> Result<(), Errno> {
1818    log_trace!(
1819        target:% = target.path(&current_task.fs()),
1820        flags:?;
1821        "do_mount_change_propagation_type",
1822    );
1823
1824    // Flag validation. Of the three propagation type flags, exactly one must be passed. The only
1825    // valid flags other than propagation type are MS_SILENT and MS_REC.
1826    //
1827    // Use if statements to find the first propagation type flag, then check for valid flags using
1828    // only the first propagation flag and MS_REC / MS_SILENT as valid flags.
1829    let propagation_flag = if flags.contains(MountFlags::SHARED) {
1830        MountFlags::SHARED
1831    } else if flags.contains(MountFlags::PRIVATE) {
1832        MountFlags::PRIVATE
1833    } else if flags.contains(MountFlags::DOWNSTREAM) {
1834        MountFlags::DOWNSTREAM
1835    } else {
1836        return error!(EINVAL);
1837    };
1838    if flags.intersects(!(propagation_flag | MountFlags::REC | MountFlags::SILENT)) {
1839        return error!(EINVAL);
1840    }
1841
1842    let mount = target.mount_if_root()?;
1843    let mounts_guard = current_task.kernel().mounts_lock();
1844    mount.change_propagation(&mounts_guard, propagation_flag, flags.contains(MountFlags::REC));
1845    Ok(())
1846}
1847
1848fn do_mount_move(
1849    current_task: &CurrentTask,
1850    source_addr: UserCString,
1851    target: NamespaceNode,
1852) -> Result<(), Errno> {
1853    let source = lookup_at(current_task, FdNumber::AT_FDCWD, source_addr, LookupFlags::default())?;
1854    let source_mount = source.mount_if_root()?;
1855    // A target node may not have a mount point if it's an anonymous node. Modern Linux (6.15+)
1856    // returns ENOENT in this case, rather than the more intuitive EINVAL.
1857    let target_mount = target.mount.as_ref().ok_or_else(|| errno!(ENOENT))?;
1858    Mount::move_mount(source_mount, target_mount, &target.entry)
1859}
1860
1861fn do_mount_create(
1862    current_task: &CurrentTask,
1863    source_addr: UserCString,
1864    target: NamespaceNode,
1865    filesystemtype_addr: UserCString,
1866    data_addr: UserCString,
1867    flags: MountFlags,
1868) -> Result<(), Errno> {
1869    let source = current_task.read_path_if_non_null(source_addr)?;
1870    let fs_type = current_task.read_path(filesystemtype_addr)?;
1871    let data = current_task.read_path_if_non_null(data_addr)?;
1872    log_trace!(
1873        source:%,
1874        target:% = target.path(&current_task.fs()),
1875        fs_type:%,
1876        data:%;
1877        "do_mount_create",
1878    );
1879
1880    let options = FileSystemOptions {
1881        source: source.into(),
1882        flags: flags.file_system_flags().into(),
1883        params: MountParams::parse(data.as_ref())?,
1884    };
1885
1886    let fs = current_task.create_filesystem(fs_type.as_ref(), options)?;
1887
1888    security::sb_kern_mount(current_task, &fs)?;
1889    target.mount(WhatToMount::Fs(fs), flags.mountpoint_flags())
1890}
1891
1892pub fn sys_umount2(
1893    current_task: &CurrentTask,
1894    target_addr: UserCString,
1895    flags: u32,
1896) -> Result<(), Errno> {
1897    security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1898
1899    let unmount_flags = UnmountFlags::from_bits(flags).ok_or_else(|| {
1900        track_stub!(
1901            TODO("https://fxbug.dev/322875327"),
1902            "unmount unknown flags",
1903            flags & !UnmountFlags::from_bits_truncate(flags).bits()
1904        );
1905        errno!(EINVAL)
1906    })?;
1907
1908    if unmount_flags.contains(UnmountFlags::EXPIRE)
1909        && (unmount_flags.contains(UnmountFlags::FORCE)
1910            || unmount_flags.contains(UnmountFlags::DETACH))
1911    {
1912        return error!(EINVAL);
1913    }
1914
1915    let lookup_flags = if unmount_flags.contains(UnmountFlags::NOFOLLOW) {
1916        LookupFlags::no_follow()
1917    } else {
1918        LookupFlags::default()
1919    };
1920    let target = lookup_at(current_task, FdNumber::AT_FDCWD, target_addr, lookup_flags)?;
1921
1922    security::sb_umount(current_task, &target, unmount_flags)?;
1923
1924    target.unmount(unmount_flags)
1925}
1926
1927pub fn sys_eventfd2(current_task: &CurrentTask, value: u32, flags: u32) -> Result<FdNumber, Errno> {
1928    if flags & !(EFD_CLOEXEC | EFD_NONBLOCK | EFD_SEMAPHORE) != 0 {
1929        return error!(EINVAL);
1930    }
1931    let blocking = (flags & EFD_NONBLOCK) == 0;
1932    let eventfd_type =
1933        if (flags & EFD_SEMAPHORE) == 0 { EventFdType::Counter } else { EventFdType::Semaphore };
1934    let file = new_eventfd(current_task, value, eventfd_type, blocking);
1935    let fd_flags = if flags & EFD_CLOEXEC != 0 { FdFlags::CLOEXEC } else { FdFlags::empty() };
1936    let fd = current_task.add_file(file, fd_flags)?;
1937    Ok(fd)
1938}
1939
1940pub fn sys_pidfd_open(
1941    current_task: &CurrentTask,
1942    pid: pid_t,
1943    flags: u32,
1944) -> Result<FdNumber, Errno> {
1945    if flags & !PIDFD_NONBLOCK != 0 {
1946        return error!(EINVAL);
1947    }
1948    if pid <= 0 {
1949        return error!(EINVAL);
1950    }
1951
1952    let file = {
1953        let pid_table = current_task.kernel().pids.read();
1954
1955        let blocking = (flags & PIDFD_NONBLOCK) == 0;
1956        let open_flags = if blocking { OpenFlags::empty() } else { OpenFlags::NONBLOCK };
1957
1958        // Validate that a process (and not just a task) entry exists for the PID.
1959        let task = pid_table.get_task(pid).ok();
1960        let file = match (pid_table.get_process(pid), task) {
1961            (Some(ProcessEntryRef::Process(proc)), Some(task)) => {
1962                new_pidfd(current_task, &proc, &*task.mm()?, open_flags)
1963            }
1964            (Some(ProcessEntryRef::Zombie(_)), _) => new_zombie_pidfd(current_task, open_flags),
1965            (None, Some(_)) => return error!(EINVAL),
1966            _ => return error!(ESRCH),
1967        };
1968        file
1969    };
1970
1971    current_task.add_file(file, FdFlags::CLOEXEC)
1972}
1973
1974pub fn sys_pidfd_getfd(
1975    current_task: &CurrentTask,
1976    pidfd: FdNumber,
1977    targetfd: FdNumber,
1978    flags: u32,
1979) -> Result<FdNumber, Errno> {
1980    if flags != 0 {
1981        return error!(EINVAL);
1982    }
1983
1984    let file = current_task.files().get(pidfd)?;
1985    let tg = file.as_thread_group_key()?;
1986    let tg = tg.upgrade().ok_or_else(|| errno!(ESRCH))?;
1987    let task = tg.read().get_running_task()?;
1988
1989    current_task.check_ptrace_access_mode(PTRACE_MODE_ATTACH_REALCREDS, &task)?;
1990
1991    let target_file = task.files()?.get(targetfd)?;
1992    current_task.add_file(target_file, FdFlags::CLOEXEC)
1993}
1994
1995pub fn sys_timerfd_create(
1996    current_task: &CurrentTask,
1997    clock_id: u32,
1998    flags: u32,
1999) -> Result<FdNumber, Errno> {
2000    let timeline = match clock_id {
2001        CLOCK_MONOTONIC => Timeline::Monotonic,
2002        CLOCK_BOOTTIME | CLOCK_BOOTTIME_ALARM => Timeline::BootInstant,
2003        CLOCK_REALTIME | CLOCK_REALTIME_ALARM => Timeline::RealTime,
2004        _ => return error!(EINVAL),
2005    };
2006    let timer_type = match clock_id {
2007        CLOCK_MONOTONIC | CLOCK_BOOTTIME | CLOCK_REALTIME => TimerWakeup::Regular,
2008        CLOCK_BOOTTIME_ALARM | CLOCK_REALTIME_ALARM => {
2009            security::check_task_capable(current_task, CAP_WAKE_ALARM)?;
2010            TimerWakeup::Alarm
2011        }
2012        _ => return error!(EINVAL),
2013    };
2014    if flags & !(TFD_NONBLOCK | TFD_CLOEXEC) != 0 {
2015        track_stub!(TODO("https://fxbug.dev/322875488"), "timerfd_create unknown flags", flags);
2016        return error!(EINVAL);
2017    }
2018    log_trace!("timerfd_create(clock_id={:?}, flags={:#x})", clock_id, flags);
2019
2020    let mut open_flags = OpenFlags::RDWR;
2021    if flags & TFD_NONBLOCK != 0 {
2022        open_flags |= OpenFlags::NONBLOCK;
2023    }
2024
2025    let mut fd_flags = FdFlags::empty();
2026    if flags & TFD_CLOEXEC != 0 {
2027        fd_flags |= FdFlags::CLOEXEC;
2028    };
2029
2030    let timer = TimerFile::new_file(current_task, timer_type, timeline, open_flags)?;
2031    let fd = current_task.add_file(timer, fd_flags)?;
2032    Ok(fd)
2033}
2034
2035pub fn sys_timerfd_gettime(
2036    current_task: &CurrentTask,
2037    fd: FdNumber,
2038    user_current_value: ITimerSpecPtr,
2039) -> Result<(), Errno> {
2040    let file = current_task.files().get(fd)?;
2041    let timer_file = file.downcast_file::<TimerFile>().ok_or_else(|| errno!(EINVAL))?;
2042    let timer_info = timer_file.current_timer_spec();
2043    log_trace!("timerfd_gettime(fd={:?}, current_value={:?})", fd, timer_info);
2044    current_task.write_multi_arch_object(user_current_value, timer_info)?;
2045    Ok(())
2046}
2047
2048pub fn sys_timerfd_settime(
2049    current_task: &CurrentTask,
2050    fd: FdNumber,
2051    flags: u32,
2052    user_new_value: ITimerSpecPtr,
2053    user_old_value: ITimerSpecPtr,
2054) -> Result<(), Errno> {
2055    if flags & !(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET) != 0 {
2056        track_stub!(TODO("https://fxbug.dev/322874722"), "timerfd_settime unknown flags", flags);
2057        return error!(EINVAL);
2058    }
2059
2060    let file = current_task.files().get(fd)?;
2061    let timer_file = file.downcast_file::<TimerFile>().ok_or_else(|| errno!(EINVAL))?;
2062
2063    if timer_file.wakeup_type() == TimerWakeup::Alarm {
2064        security::check_task_capable(current_task, CAP_WAKE_ALARM)?;
2065    }
2066
2067    let new_timer_spec = current_task.read_multi_arch_object(user_new_value)?;
2068    let old_timer_spec = timer_file.set_timer_spec(current_task, &file, new_timer_spec, flags)?;
2069    log_trace!(
2070        "timerfd_settime(fd={:?}, flags={:#x}, new_value={:?}, current_value={:?})",
2071        fd,
2072        flags,
2073        new_timer_spec,
2074        old_timer_spec
2075    );
2076    if !user_old_value.is_null() {
2077        current_task.write_multi_arch_object(user_old_value, old_timer_spec)?;
2078    }
2079    Ok(())
2080}
2081
2082fn deadline_after_timespec(
2083    current_task: &CurrentTask,
2084    user_timespec: TimeSpecPtr,
2085) -> Result<zx::MonotonicInstant, Errno> {
2086    if user_timespec.is_null() {
2087        Ok(zx::MonotonicInstant::INFINITE)
2088    } else {
2089        let timespec = current_task.read_multi_arch_object(user_timespec)?;
2090        Ok(zx::MonotonicInstant::after(duration_from_timespec(timespec)?))
2091    }
2092}
2093
2094static_assertions::assert_eq_size!(uapi::__kernel_fd_set, uapi::arch32::__kernel_fd_set);
2095
2096fn select(
2097    current_task: &mut CurrentTask,
2098    nfds: u32,
2099    readfds_addr: UserRef<__kernel_fd_set>,
2100    writefds_addr: UserRef<__kernel_fd_set>,
2101    exceptfds_addr: UserRef<__kernel_fd_set>,
2102    deadline: zx::MonotonicInstant,
2103    sigmask_addr: UserRef<pselect6_sigmask>,
2104) -> Result<i32, Errno> {
2105    const BITS_PER_BYTE: usize = 8;
2106
2107    fn sizeof<T>(_: &T) -> usize {
2108        BITS_PER_BYTE * std::mem::size_of::<T>()
2109    }
2110    fn is_fd_set(set: &__kernel_fd_set, fd: usize) -> bool {
2111        let index = fd / sizeof(&set.fds_bits[0]);
2112        let remainder = fd % sizeof(&set.fds_bits[0]);
2113        set.fds_bits[index] & (1 << remainder) > 0
2114    }
2115    fn add_fd_to_set(set: &mut __kernel_fd_set, fd: usize) {
2116        let index = fd / sizeof(&set.fds_bits[0]);
2117        let remainder = fd % sizeof(&set.fds_bits[0]);
2118
2119        set.fds_bits[index] |= 1 << remainder;
2120    }
2121    let read_fd_set = |addr: UserRef<__kernel_fd_set>| {
2122        if addr.is_null() { Ok(Default::default()) } else { current_task.read_object(addr) }
2123    };
2124
2125    if nfds as usize > BITS_PER_BYTE * std::mem::size_of::<__kernel_fd_set>() {
2126        return error!(EINVAL);
2127    }
2128
2129    let read_events =
2130        FdEvents::from_bits_truncate(POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR);
2131    let write_events = FdEvents::from_bits_truncate(POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR);
2132    let except_events = FdEvents::from_bits_truncate(POLLPRI);
2133
2134    let readfds = read_fd_set(readfds_addr)?;
2135    let writefds = read_fd_set(writefds_addr)?;
2136    let exceptfds = read_fd_set(exceptfds_addr)?;
2137
2138    let sets = &[(read_events, &readfds), (write_events, &writefds), (except_events, &exceptfds)];
2139    let waiter = FileWaiter::<FdNumber>::default();
2140
2141    for fd in 0..nfds {
2142        let mut aggregated_events = FdEvents::empty();
2143        for (events, fds) in sets.iter() {
2144            if is_fd_set(fds, fd as usize) {
2145                aggregated_events |= *events;
2146            }
2147        }
2148        if !aggregated_events.is_empty() {
2149            let fd = FdNumber::from_raw(fd as i32);
2150            let file = current_task.files().get(fd)?;
2151            waiter.add(current_task, fd, Some(&file), aggregated_events)?;
2152        }
2153    }
2154
2155    let mask = if !sigmask_addr.is_null() {
2156        let sigmask = current_task.read_object(sigmask_addr)?;
2157        let mask = if sigmask.ss.is_null() {
2158            current_task.read().signal_mask()
2159        } else {
2160            if sigmask.ss_len < std::mem::size_of::<sigset_t>() {
2161                return error!(EINVAL);
2162            }
2163            current_task.read_object(sigmask.ss.into())?
2164        };
2165        Some(mask)
2166    } else {
2167        None
2168    };
2169
2170    waiter.wait(current_task, mask, deadline)?;
2171
2172    let mut num_fds = 0;
2173    let mut readfds_out: __kernel_fd_set = Default::default();
2174    let mut writefds_out: __kernel_fd_set = Default::default();
2175    let mut exceptfds_out: __kernel_fd_set = Default::default();
2176    let mut sets = [
2177        (read_events, &readfds, &mut readfds_out),
2178        (write_events, &writefds, &mut writefds_out),
2179        (except_events, &exceptfds, &mut exceptfds_out),
2180    ];
2181    let mut ready_items = waiter.ready_items.lock();
2182    for ReadyItem { key: ready_key, events: ready_events } in ready_items.drain(..) {
2183        let ready_key = assert_matches::assert_matches!(
2184            ready_key,
2185            ReadyItemKey::FdNumber(v) => v
2186        );
2187
2188        sets.iter_mut().for_each(|(events, fds, fds_out)| {
2189            let fd = ready_key.raw() as usize;
2190            if events.intersects(ready_events) && is_fd_set(fds, fd) {
2191                add_fd_to_set(fds_out, fd);
2192                num_fds += 1;
2193            }
2194        });
2195    }
2196
2197    let write_fd_set =
2198        |addr: UserRef<__kernel_fd_set>, value: __kernel_fd_set| -> Result<(), Errno> {
2199            if !addr.is_null() {
2200                current_task.write_object(addr, &value)?;
2201            }
2202            Ok(())
2203        };
2204    write_fd_set(readfds_addr, readfds_out)?;
2205    write_fd_set(writefds_addr, writefds_out)?;
2206    write_fd_set(exceptfds_addr, exceptfds_out)?;
2207    Ok(num_fds)
2208}
2209
2210pub fn sys_pselect6(
2211    current_task: &mut CurrentTask,
2212    nfds: u32,
2213    readfds_addr: UserRef<__kernel_fd_set>,
2214    writefds_addr: UserRef<__kernel_fd_set>,
2215    exceptfds_addr: UserRef<__kernel_fd_set>,
2216    timeout_addr: TimeSpecPtr,
2217    sigmask_addr: UserRef<pselect6_sigmask>,
2218) -> Result<i32, Errno> {
2219    let deadline = deadline_after_timespec(current_task, timeout_addr)?;
2220
2221    let num_fds = select(
2222        current_task,
2223        nfds,
2224        readfds_addr,
2225        writefds_addr,
2226        exceptfds_addr,
2227        deadline,
2228        sigmask_addr,
2229    )?;
2230
2231    if !timeout_addr.is_null()
2232        && !current_task
2233            .thread_group()
2234            .read()
2235            .personality
2236            .contains(PersonalityFlags::STICKY_TIMEOUTS)
2237    {
2238        let now = zx::MonotonicInstant::get();
2239        let remaining = std::cmp::max(deadline - now, zx::MonotonicDuration::from_seconds(0));
2240        current_task.write_multi_arch_object(timeout_addr, timespec_from_duration(remaining))?;
2241    }
2242
2243    Ok(num_fds)
2244}
2245
2246pub fn sys_select(
2247    current_task: &mut CurrentTask,
2248    nfds: u32,
2249    readfds_addr: UserRef<__kernel_fd_set>,
2250    writefds_addr: UserRef<__kernel_fd_set>,
2251    exceptfds_addr: UserRef<__kernel_fd_set>,
2252    timeout_addr: TimeValPtr,
2253) -> Result<i32, Errno> {
2254    let start_time = zx::MonotonicInstant::get();
2255
2256    let deadline = if timeout_addr.is_null() {
2257        zx::MonotonicInstant::INFINITE
2258    } else {
2259        let timeval = current_task.read_multi_arch_object(timeout_addr)?;
2260        start_time + starnix_types::time::duration_from_timeval(timeval)?
2261    };
2262
2263    let num_fds = select(
2264        current_task,
2265        nfds,
2266        readfds_addr,
2267        writefds_addr,
2268        exceptfds_addr,
2269        deadline,
2270        UserRef::<pselect6_sigmask>::default(),
2271    )?;
2272
2273    if !timeout_addr.is_null()
2274        && !current_task
2275            .thread_group()
2276            .read()
2277            .personality
2278            .contains(PersonalityFlags::STICKY_TIMEOUTS)
2279    {
2280        let now = zx::MonotonicInstant::get();
2281        let remaining = std::cmp::max(deadline - now, zx::MonotonicDuration::from_seconds(0));
2282        current_task.write_multi_arch_object(
2283            timeout_addr,
2284            starnix_types::time::timeval_from_duration(remaining),
2285        )?;
2286    }
2287
2288    Ok(num_fds)
2289}
2290
2291pub fn sys_epoll_create1(current_task: &CurrentTask, flags: u32) -> Result<FdNumber, Errno> {
2292    if flags & !EPOLL_CLOEXEC != 0 {
2293        return error!(EINVAL);
2294    }
2295    let ep_file = EpollFileObject::new_file(current_task);
2296    let fd_flags = if flags & EPOLL_CLOEXEC != 0 { FdFlags::CLOEXEC } else { FdFlags::empty() };
2297    let fd = current_task.add_file(ep_file, fd_flags)?;
2298    Ok(fd)
2299}
2300
2301pub fn sys_epoll_ctl(
2302    current_task: &CurrentTask,
2303    epfd: FdNumber,
2304    op: u32,
2305    fd: FdNumber,
2306    event: UserRef<EpollEvent>,
2307) -> Result<(), Errno> {
2308    let file = current_task.files().get(epfd)?;
2309    let epoll_file = file.downcast_file::<EpollFileObject>().ok_or_else(|| errno!(EINVAL))?;
2310    let operand_file = current_task.files().get(fd)?;
2311
2312    if Arc::ptr_eq(&file, &operand_file) {
2313        return error!(EINVAL);
2314    }
2315
2316    let epoll_event = match current_task.read_object(event) {
2317        Ok(mut epoll_event) => {
2318            // If EPOLLWAKEUP is specified in flags, but the caller does not have the CAP_BLOCK_SUSPEND
2319            // capability, then the EPOLLWAKEUP flag is silently ignored.
2320            // See https://man7.org/linux/man-pages/man2/epoll_ctl.2.html
2321            if epoll_event.events().contains(FdEvents::EPOLLWAKEUP) {
2322                if !security::is_task_capable_noaudit(current_task, CAP_BLOCK_SUSPEND) {
2323                    epoll_event.ignore(FdEvents::EPOLLWAKEUP);
2324                }
2325            }
2326            Ok(epoll_event)
2327        }
2328        result => result,
2329    };
2330
2331    match op {
2332        EPOLL_CTL_ADD => {
2333            epoll_file.add(current_task, &operand_file, &file, epoll_event?)?;
2334            operand_file.register_epfd(&file);
2335        }
2336        EPOLL_CTL_MOD => {
2337            epoll_file.modify(current_task, &operand_file, epoll_event?)?;
2338        }
2339        EPOLL_CTL_DEL => {
2340            epoll_file.delete(current_task, &operand_file)?;
2341            operand_file.unregister_epfd(&file);
2342        }
2343        _ => return error!(EINVAL),
2344    }
2345    Ok(())
2346}
2347
2348// Backend for sys_epoll_pwait and sys_epoll_pwait2 that takes an already-decoded deadline.
2349fn do_epoll_pwait(
2350    current_task: &mut CurrentTask,
2351    epfd: FdNumber,
2352    events: UserRef<EpollEvent>,
2353    unvalidated_max_events: i32,
2354    deadline: zx::MonotonicInstant,
2355    user_sigmask: UserRef<SigSet>,
2356) -> Result<usize, Errno> {
2357    let file = current_task.files().get(epfd)?;
2358    let epoll_file = file.downcast_file::<EpollFileObject>().ok_or_else(|| errno!(EINVAL))?;
2359
2360    // Max_events must be greater than 0.
2361    let max_events: usize = unvalidated_max_events.try_into().map_err(|_| errno!(EINVAL))?;
2362    if max_events == 0 {
2363        return error!(EINVAL);
2364    }
2365
2366    // Return early if the user passes an obviously invalid pointer. This avoids dropping events
2367    // for common pointer errors. When we catch bad pointers after the wait is complete when the
2368    // memory is actually written, the events will be lost. This check is not a guarantee.
2369    current_task
2370        .mm()?
2371        .check_plausible(events.addr(), max_events * std::mem::size_of::<EpollEvent>())?;
2372
2373    let active_events = if !user_sigmask.is_null() {
2374        let signal_mask = current_task.read_object(user_sigmask)?;
2375        current_task.wait_with_temporary_mask(signal_mask, |current_task| {
2376            epoll_file.wait(current_task, max_events, deadline)
2377        })?
2378    } else {
2379        epoll_file.wait(current_task, max_events, deadline)?
2380    };
2381
2382    current_task.write_objects(events, &active_events)?;
2383    Ok(active_events.len())
2384}
2385
2386pub fn sys_epoll_pwait(
2387    current_task: &mut CurrentTask,
2388    epfd: FdNumber,
2389    events: UserRef<EpollEvent>,
2390    max_events: i32,
2391    timeout: i32,
2392    user_sigmask: UserRef<SigSet>,
2393) -> Result<usize, Errno> {
2394    let deadline = zx::MonotonicInstant::after(duration_from_poll_timeout(timeout)?);
2395    do_epoll_pwait(current_task, epfd, events, max_events, deadline, user_sigmask)
2396}
2397
2398pub fn sys_epoll_pwait2(
2399    current_task: &mut CurrentTask,
2400    epfd: FdNumber,
2401    events: UserRef<EpollEvent>,
2402    max_events: i32,
2403    user_timespec: TimeSpecPtr,
2404    user_sigmask: UserRef<SigSet>,
2405) -> Result<usize, Errno> {
2406    let deadline = deadline_after_timespec(current_task, user_timespec)?;
2407    do_epoll_pwait(current_task, epfd, events, max_events, deadline, user_sigmask)
2408}
2409
2410struct FileWaiter<Key: Into<ReadyItemKey>> {
2411    waiter: Waiter,
2412    ready_items: Arc<LockDepMutex<VecDeque<ReadyItem>, EventHandlerReadyQueueLock>>,
2413    _marker: PhantomData<Key>,
2414}
2415
2416impl<Key: Into<ReadyItemKey>> Default for FileWaiter<Key> {
2417    fn default() -> Self {
2418        Self { waiter: Waiter::new(), ready_items: Default::default(), _marker: PhantomData }
2419    }
2420}
2421
2422impl<Key: Into<ReadyItemKey>> FileWaiter<Key> {
2423    fn add(
2424        &self,
2425        current_task: &CurrentTask,
2426        key: Key,
2427        file: Option<&FileHandle>,
2428        requested_events: FdEvents,
2429    ) -> Result<(), Errno> {
2430        let key = key.into();
2431
2432        if let Some(file) = file {
2433            let sought_events = requested_events | FdEvents::POLLERR | FdEvents::POLLHUP;
2434
2435            let handler =
2436                EventHandler::Enqueue { key, queue: self.ready_items.clone(), sought_events };
2437            file.wait_async(current_task, &self.waiter, sought_events, handler);
2438            let current_events = file.query_events(current_task)? & sought_events;
2439            if !current_events.is_empty() {
2440                self.ready_items.lock().push_back(ReadyItem { key, events: current_events });
2441            }
2442        } else {
2443            self.ready_items.lock().push_back(ReadyItem { key, events: FdEvents::POLLNVAL });
2444        }
2445        Ok(())
2446    }
2447
2448    fn wait(
2449        &self,
2450        current_task: &mut CurrentTask,
2451        signal_mask: Option<SigSet>,
2452        deadline: zx::MonotonicInstant,
2453    ) -> Result<(), Errno> {
2454        if self.ready_items.lock().is_empty() {
2455            // When wait_until() returns Ok() it means there was a wake up; however there may not
2456            // be a ready item, for example if waiting on a sync file with multiple sync points.
2457            // Keep waiting until there's at least one ready item.
2458            let signal_mask = signal_mask.unwrap_or_else(|| current_task.read().signal_mask());
2459            let mut result = current_task.wait_with_temporary_mask(signal_mask, |current_task| {
2460                self.waiter.wait_until(current_task, deadline)
2461            });
2462            loop {
2463                match result {
2464                    Err(err) if err == ETIMEDOUT => return Ok(()),
2465                    Ok(()) => {
2466                        if !self.ready_items.lock().is_empty() {
2467                            break;
2468                        }
2469                    }
2470                    result => result?,
2471                };
2472                result = self.waiter.wait_until(current_task, deadline);
2473            }
2474        }
2475        Ok(())
2476    }
2477}
2478
2479pub fn poll(
2480    current_task: &mut CurrentTask,
2481    user_pollfds: UserRef<pollfd>,
2482    num_fds: i32,
2483    mask: Option<SigSet>,
2484    deadline: zx::MonotonicInstant,
2485) -> Result<usize, Errno> {
2486    if num_fds < 0 || num_fds as u64 > current_task.thread_group().get_rlimit(Resource::NOFILE) {
2487        return error!(EINVAL);
2488    }
2489
2490    let mut pollfds = vec![pollfd::default(); num_fds as usize];
2491    let waiter = FileWaiter::<usize>::default();
2492
2493    for (index, poll_descriptor) in pollfds.iter_mut().enumerate() {
2494        *poll_descriptor = current_task.read_object(user_pollfds.at(index)?)?;
2495        poll_descriptor.revents = 0;
2496        if poll_descriptor.fd < 0 {
2497            continue;
2498        }
2499        let file = current_task.files().get(FdNumber::from_raw(poll_descriptor.fd)).ok();
2500        waiter.add(
2501            current_task,
2502            index,
2503            file.as_ref(),
2504            FdEvents::from_bits_truncate(poll_descriptor.events as u32),
2505        )?;
2506    }
2507
2508    waiter.wait(current_task, mask, deadline)?;
2509
2510    let mut ready_items = waiter.ready_items.lock();
2511    let mut unique_ready_items =
2512        bit_vec::BitVec::from_elem(usize::try_from(num_fds).unwrap(), false);
2513    for ReadyItem { key: ready_key, events: ready_events } in ready_items.drain(..) {
2514        let ready_key = assert_matches::assert_matches!(
2515            ready_key,
2516            ReadyItemKey::Usize(v) => v
2517        );
2518        let interested_events = FdEvents::from_bits_truncate(pollfds[ready_key].events as u32)
2519            | FdEvents::POLLERR
2520            | FdEvents::POLLHUP
2521            | FdEvents::POLLNVAL;
2522        let return_events = (interested_events & ready_events).bits();
2523        pollfds[ready_key].revents = return_events as i16;
2524        unique_ready_items.set(ready_key, true);
2525    }
2526
2527    for (index, poll_descriptor) in pollfds.iter().enumerate() {
2528        current_task.write_object(user_pollfds.at(index)?, poll_descriptor)?;
2529    }
2530
2531    Ok(unique_ready_items.into_iter().filter(Clone::clone).count())
2532}
2533
2534pub fn sys_ppoll(
2535    current_task: &mut CurrentTask,
2536    user_fds: UserRef<pollfd>,
2537    num_fds: i32,
2538    user_timespec: TimeSpecPtr,
2539    user_mask: UserRef<SigSet>,
2540    sigset_size: usize,
2541) -> Result<usize, Errno> {
2542    let start_time = zx::MonotonicInstant::get();
2543
2544    let timeout = if user_timespec.is_null() {
2545        // Passing -1 to poll is equivalent to an infinite timeout.
2546        -1
2547    } else {
2548        let ts = current_task.read_multi_arch_object(user_timespec)?;
2549        duration_from_timespec::<zx::MonotonicTimeline>(ts)?.into_millis() as i32
2550    };
2551
2552    let deadline = start_time + duration_from_poll_timeout(timeout)?;
2553
2554    let mask = if !user_mask.is_null() {
2555        if sigset_size != std::mem::size_of::<SigSet>() {
2556            return error!(EINVAL);
2557        }
2558        let mask = current_task.read_object(user_mask)?;
2559        Some(mask)
2560    } else {
2561        None
2562    };
2563
2564    let poll_result = poll(current_task, user_fds, num_fds, mask, deadline);
2565
2566    if user_timespec.is_null() {
2567        return poll_result;
2568    }
2569
2570    let now = zx::MonotonicInstant::get();
2571    let remaining = std::cmp::max(deadline - now, zx::MonotonicDuration::from_seconds(0));
2572    let remaining_timespec = timespec_from_duration(remaining);
2573
2574    // From gVisor: "ppoll is normally restartable if interrupted by something other than a signal
2575    // handled by the application (i.e. returns ERESTARTNOHAND). However, if
2576    // [copy out] failed, then the restarted ppoll would use the wrong timeout, so the
2577    // error should be left as EINTR."
2578    match (current_task.write_multi_arch_object(user_timespec, remaining_timespec), poll_result) {
2579        // If write was ok, and poll was ok, return poll result.
2580        (Ok(_), Ok(num_events)) => Ok(num_events),
2581        (Ok(_), Err(e)) if e == EINTR => {
2582            error!(ERESTARTNOHAND)
2583        }
2584        (Ok(_), poll_result) => poll_result,
2585        // If write was a failure, return the poll result unchanged.
2586        (Err(_), poll_result) => poll_result,
2587    }
2588}
2589
2590pub fn sys_flock(current_task: &CurrentTask, fd: FdNumber, operation: u32) -> Result<(), Errno> {
2591    let file = current_task.files().get(fd)?;
2592    let operation = FlockOperation::from_flags(operation)?;
2593    file.flock(current_task, operation)
2594}
2595
2596pub fn sys_sync(current_task: &CurrentTask) -> Result<(), Errno> {
2597    current_task.kernel().mounts.sync_all(current_task)
2598}
2599
2600pub fn sys_syncfs(current_task: &CurrentTask, fd: FdNumber) -> Result<(), Errno> {
2601    let file = current_task.files().get(fd)?;
2602    file.fs.sync(current_task)
2603}
2604
2605pub fn sys_fsync(current_task: &CurrentTask, fd: FdNumber) -> Result<(), Errno> {
2606    let file = current_task.files().get(fd)?;
2607    file.sync(current_task)
2608}
2609
2610pub fn sys_fdatasync(current_task: &CurrentTask, fd: FdNumber) -> Result<(), Errno> {
2611    let file = current_task.files().get(fd)?;
2612    file.data_sync(current_task)
2613}
2614
2615pub fn sys_sync_file_range(
2616    current_task: &CurrentTask,
2617    fd: FdNumber,
2618    offset: off_t,
2619    length: off_t,
2620    flags: u32,
2621) -> Result<(), Errno> {
2622    const KNOWN_FLAGS: u32 = uapi::SYNC_FILE_RANGE_WAIT_BEFORE
2623        | uapi::SYNC_FILE_RANGE_WRITE
2624        | uapi::SYNC_FILE_RANGE_WAIT_AFTER;
2625    if flags & !KNOWN_FLAGS != 0 {
2626        return error!(EINVAL);
2627    }
2628
2629    let file = current_task.files().get(fd)?;
2630
2631    if offset < 0 || length < 0 {
2632        return error!(EINVAL);
2633    }
2634
2635    checked_add_offset_and_length(offset as usize, length as usize)?;
2636
2637    // From <https://linux.die.net/man/2/sync_file_range>:
2638    //
2639    //   fd refers to something other than a regular file, a block device, a directory, or a symbolic link.
2640    let mode = file.node().info().mode;
2641    if !mode.is_reg() && !mode.is_blk() && !mode.is_dir() && !mode.is_lnk() {
2642        return error!(ESPIPE);
2643    }
2644
2645    if flags == 0 {
2646        return Ok(());
2647    }
2648
2649    // Syncing the whole file is much more than we need for sync_file_range, which only needs to
2650    // sync the specified data range.
2651    file.data_sync(current_task)
2652}
2653
2654pub fn sys_fadvise64(
2655    current_task: &CurrentTask,
2656    fd: FdNumber,
2657    offset: off_t,
2658    len: off_t,
2659    advice: u32,
2660) -> Result<(), Errno> {
2661    match advice {
2662        POSIX_FADV_NORMAL => track_stub!(TODO("https://fxbug.dev/297434181"), "POSIX_FADV_NORMAL"),
2663        POSIX_FADV_RANDOM => track_stub!(TODO("https://fxbug.dev/297434181"), "POSIX_FADV_RANDOM"),
2664        POSIX_FADV_SEQUENTIAL => {
2665            track_stub!(TODO("https://fxbug.dev/297434181"), "POSIX_FADV_SEQUENTIAL")
2666        }
2667        POSIX_FADV_WILLNEED => {
2668            track_stub!(TODO("https://fxbug.dev/297434181"), "POSIX_FADV_WILLNEED")
2669        }
2670        POSIX_FADV_DONTNEED => {
2671            track_stub!(TODO("https://fxbug.dev/297434181"), "POSIX_FADV_DONTNEED")
2672        }
2673        POSIX_FADV_NOREUSE => {
2674            track_stub!(TODO("https://fxbug.dev/297434181"), "POSIX_FADV_NOREUSE")
2675        }
2676        _ => {
2677            track_stub!(TODO("https://fxbug.dev/322875684"), "fadvise64 unknown advice", advice);
2678            return error!(EINVAL);
2679        }
2680    }
2681
2682    if offset < 0 || len < 0 {
2683        return error!(EINVAL);
2684    }
2685
2686    let file = current_task.files().get(fd)?;
2687    // fadvise does not work on pipes.
2688    if file.downcast_file::<PipeFileObject>().is_some() {
2689        return error!(ESPIPE);
2690    }
2691
2692    // fadvise does not work on paths.
2693    if file.flags().contains(OpenFlags::PATH) {
2694        return error!(EBADF);
2695    }
2696
2697    Ok(())
2698}
2699
2700pub fn sys_fallocate(
2701    current_task: &CurrentTask,
2702    fd: FdNumber,
2703    mode: u32,
2704    offset: off_t,
2705    len: off_t,
2706) -> Result<(), Errno> {
2707    let file = current_task.files().get(fd)?;
2708
2709    // Offset must not be less than 0.
2710    // Length must not be less than or equal to 0.
2711    // See https://man7.org/linux/man-pages/man2/fallocate.2.html#ERRORS
2712    if offset < 0 || len <= 0 {
2713        return error!(EINVAL);
2714    }
2715
2716    let mode = FallocMode::from_bits(mode).ok_or_else(|| errno!(EINVAL))?;
2717    file.fallocate(current_task, mode, offset as u64, len as u64)?;
2718
2719    Ok(())
2720}
2721
2722pub fn sys_utimensat(
2723    current_task: &CurrentTask,
2724    dir_fd: FdNumber,
2725    user_path: UserCString,
2726    user_times: TimeSpecPtr,
2727    flags: u32,
2728) -> Result<(), Errno> {
2729    let (atime, mtime) = if user_times.addr().is_null() {
2730        // If user_times is null, the timestamps are updated to the current time.
2731        (TimeUpdateType::Now, TimeUpdateType::Now)
2732    } else {
2733        let ts = current_task.read_multi_arch_objects_to_vec(user_times, 2)?;
2734        let atime = ts[0];
2735        let mtime = ts[1];
2736        let parse_timespec = |spec: timespec| match spec.tv_nsec {
2737            UTIME_NOW => Ok(TimeUpdateType::Now),
2738            UTIME_OMIT => Ok(TimeUpdateType::Omit),
2739            _ => time_from_timespec(spec).map(TimeUpdateType::Time),
2740        };
2741        (parse_timespec(atime)?, parse_timespec(mtime)?)
2742    };
2743
2744    if let (TimeUpdateType::Omit, TimeUpdateType::Omit) = (atime, mtime) {
2745        return Ok(());
2746    };
2747
2748    // Non-standard feature: if user_path is null, the timestamps are updated on the file referred
2749    // to by dir_fd.
2750    // See https://man7.org/linux/man-pages/man2/utimensat.2.html
2751    let name = if user_path.addr().is_null() {
2752        if dir_fd == FdNumber::AT_FDCWD {
2753            return error!(EFAULT);
2754        }
2755        let (node, _) =
2756            current_task.resolve_dir_fd(dir_fd, Default::default(), ResolveFlags::empty())?;
2757        node
2758    } else {
2759        let lookup_flags = LookupFlags::from_bits(flags, AT_SYMLINK_NOFOLLOW)?;
2760        lookup_at(current_task, dir_fd, user_path, lookup_flags)?
2761    };
2762    name.entry.node.update_atime_mtime(current_task, &name.mount, atime, mtime)?;
2763    let event_mask = match (atime, mtime) {
2764        (_, TimeUpdateType::Omit) => InotifyMask::ACCESS,
2765        (TimeUpdateType::Omit, _) => InotifyMask::MODIFY,
2766        (_, _) => InotifyMask::ATTRIB,
2767    };
2768    name.entry.notify_ignoring_excl_unlink(event_mask);
2769    Ok(())
2770}
2771
2772pub fn sys_splice(
2773    current_task: &CurrentTask,
2774    fd_in: FdNumber,
2775    off_in: OffsetPtr,
2776    fd_out: FdNumber,
2777    off_out: OffsetPtr,
2778    len: usize,
2779    flags: u32,
2780) -> Result<usize, Errno> {
2781    splice::splice(current_task, fd_in, off_in, fd_out, off_out, len, flags)
2782}
2783
2784pub fn sys_vmsplice(
2785    current_task: &CurrentTask,
2786    fd: FdNumber,
2787    iovec_addr: IOVecPtr,
2788    iovec_count: UserValue<i32>,
2789    flags: u32,
2790) -> Result<usize, Errno> {
2791    splice::vmsplice(current_task, fd, iovec_addr, iovec_count, flags)
2792}
2793
2794pub fn sys_copy_file_range(
2795    current_task: &CurrentTask,
2796    fd_in: FdNumber,
2797    off_in: OffsetPtr,
2798    fd_out: FdNumber,
2799    off_out: OffsetPtr,
2800    len: usize,
2801    flags: u32,
2802) -> Result<usize, Errno> {
2803    splice::copy_file_range(current_task, fd_in, off_in, fd_out, off_out, len, flags)
2804}
2805
2806pub fn sys_tee(
2807    current_task: &CurrentTask,
2808    fd_in: FdNumber,
2809    fd_out: FdNumber,
2810    len: usize,
2811    flags: u32,
2812) -> Result<usize, Errno> {
2813    splice::tee(current_task, fd_in, fd_out, len, flags)
2814}
2815
2816pub fn sys_readahead(
2817    current_task: &CurrentTask,
2818    fd: FdNumber,
2819    offset: off_t,
2820    length: usize,
2821) -> Result<(), Errno> {
2822    let file = current_task.files().get(fd)?;
2823    // Allow only non-negative values of `offset`. Some versions of Linux allow it to be negative,
2824    // but GVisor tests require `readahead()` to fail in this case.
2825    let offset: usize = offset.try_into().map_err(|_| errno!(EINVAL))?;
2826    file.readahead(current_task, offset, length)
2827}
2828
2829pub fn sys_io_setup(
2830    current_task: &CurrentTask,
2831    user_nr_events: UserValue<u32>,
2832    user_ctx_idp: MultiArchUserRef<uapi::aio_context_t, uapi::arch32::aio_context_t>,
2833) -> Result<(), Errno> {
2834    // From https://man7.org/linux/man-pages/man2/io_setup.2.html:
2835    //
2836    //   EINVAL ctx_idp is not initialized, or the specified nr_events
2837    //   exceeds internal limits.  nr_events should be greater than
2838    //   0.
2839    //
2840    // TODO: Determine what "internal limits" means.
2841    let max_operations =
2842        user_nr_events.validate(0..(i32::MAX as u32)).ok_or_else(|| errno!(EINVAL))? as usize;
2843    if current_task.read_multi_arch_object(user_ctx_idp)? != 0 {
2844        return error!(EINVAL);
2845    }
2846    let ctx_id = AioContext::create(current_task, max_operations)?;
2847    current_task.write_multi_arch_object(user_ctx_idp, ctx_id).map_err(|e| {
2848        let _ = current_task
2849            .mm()
2850            .expect("previous sys_io_setup code verified mm exists")
2851            .destroy_aio_context(ctx_id.into());
2852        e
2853    })?;
2854    Ok(())
2855}
2856
2857pub fn sys_io_submit(
2858    current_task: &CurrentTask,
2859    ctx_id: aio_context_t,
2860    user_nr: UserValue<i32>,
2861    mut iocb_addrs: IocbPtrPtr,
2862) -> Result<i32, Errno> {
2863    let nr = user_nr.validate(0..i32::MAX).ok_or_else(|| errno!(EINVAL))?;
2864    if nr == 0 {
2865        return Ok(0);
2866    }
2867    let ctx = current_task.mm()?.get_aio_context(ctx_id.into()).ok_or_else(|| errno!(EINVAL))?;
2868
2869    // `iocbpp` is an array of addresses to iocb's.
2870    let mut num_submitted: i32 = 0;
2871    loop {
2872        let iocb_ref = current_task.read_multi_arch_ptr(iocb_addrs)?;
2873        let control_block = current_task.read_multi_arch_object(iocb_ref)?;
2874
2875        match (num_submitted, ctx.submit(current_task, control_block, iocb_ref)) {
2876            (0, Err(e)) => return Err(e),
2877            (_, Err(_)) => break,
2878            (_, Ok(())) => {
2879                num_submitted += 1;
2880                if num_submitted == nr {
2881                    break;
2882                }
2883            }
2884        };
2885
2886        iocb_addrs = iocb_addrs.next()?;
2887    }
2888
2889    Ok(num_submitted)
2890}
2891
2892pub fn sys_io_getevents(
2893    current_task: &CurrentTask,
2894    ctx_id: aio_context_t,
2895    min_nr: i64,
2896    nr: i64,
2897    events_ref: UserRef<io_event>,
2898    user_timeout: TimeSpecPtr,
2899) -> Result<i32, Errno> {
2900    if min_nr < 0 || min_nr > nr || nr < 0 {
2901        return error!(EINVAL);
2902    }
2903    let min_results = min_nr as usize;
2904    let max_results = nr as usize;
2905    let deadline = deadline_after_timespec(current_task, user_timeout)?;
2906
2907    let ctx = current_task.mm()?.get_aio_context(ctx_id.into()).ok_or_else(|| errno!(EINVAL))?;
2908    let events = ctx.get_events(current_task, min_results, max_results, deadline)?;
2909    current_task.write_objects(events_ref, &events)?;
2910
2911    Ok(events.len() as i32)
2912}
2913
2914pub fn sys_io_cancel(
2915    current_task: &CurrentTask,
2916    ctx_id: aio_context_t,
2917    user_iocb: IocbPtr,
2918    _result: UserRef<io_event>,
2919) -> Result<(), Errno> {
2920    let iocb = current_task.read_multi_arch_object(user_iocb)?;
2921    let ctx = current_task.mm()?.get_aio_context(ctx_id.into()).ok_or_else(|| errno!(EINVAL))?;
2922
2923    ctx.cancel(current_task, iocb, user_iocb)?;
2924    // TODO: Correctly handle return. If the operation is successfully canceled, the event should be copied into the memory pointed to by result without being placed into the completion queue.
2925    track_stub!(TODO("https://fxbug.dev/297433877"), "io_cancel");
2926    Ok(())
2927}
2928
2929pub fn sys_io_destroy(current_task: &CurrentTask, ctx_id: aio_context_t) -> Result<(), Errno> {
2930    let aio_context = current_task.mm()?.destroy_aio_context(ctx_id.into())?;
2931    std::mem::drop(aio_context);
2932    Ok(())
2933}
2934
2935// Syscalls for arch32 usage
2936#[cfg(target_arch = "aarch64")]
2937mod arch32 {
2938    use crate::mm::MemoryAccessorExt;
2939    use crate::task::CurrentTask;
2940    use crate::vfs::syscalls::{
2941        LookupFlags, OpenFlags, lookup_at, sys_dup3, sys_faccessat, sys_fallocate, sys_lseek,
2942        sys_mkdirat, sys_openat, sys_readlinkat, sys_unlinkat,
2943    };
2944    use crate::vfs::{FdNumber, FsNode};
2945    use linux_uapi::off_t;
2946    use starnix_syscalls::SyscallArg;
2947    use starnix_types::time::duration_from_poll_timeout;
2948    use starnix_uapi::errors::Errno;
2949    use starnix_uapi::file_mode::FileMode;
2950    use starnix_uapi::signals::SigSet;
2951    use starnix_uapi::user_address::{MultiArchUserRef, UserAddress, UserCString, UserRef};
2952    use starnix_uapi::vfs::EpollEvent;
2953    use starnix_uapi::{AT_REMOVEDIR, errno, error, uapi};
2954
2955    type StatFs64Ptr = MultiArchUserRef<uapi::statfs, uapi::arch32::statfs64>;
2956
2957    fn merge_low_and_high(low: u32, high: u32) -> off_t {
2958        ((high as off_t) << 32) | (low as off_t)
2959    }
2960
2961    pub fn sys_arch32_open(
2962        current_task: &CurrentTask,
2963        user_path: UserCString,
2964        flags: u32,
2965        mode: FileMode,
2966    ) -> Result<FdNumber, Errno> {
2967        sys_openat(current_task, FdNumber::AT_FDCWD, user_path, flags, mode)
2968    }
2969
2970    pub fn sys_arch32_access(
2971        current_task: &CurrentTask,
2972        user_path: UserCString,
2973        mode: u32,
2974    ) -> Result<(), Errno> {
2975        sys_faccessat(current_task, FdNumber::AT_FDCWD, user_path, mode)
2976    }
2977    pub fn stat64(
2978        current_task: &CurrentTask,
2979        node: &FsNode,
2980        arch32_stat_buf: UserRef<uapi::arch32::stat64>,
2981    ) -> Result<(), Errno> {
2982        let stat_buffer = node.stat(current_task)?;
2983        let result: uapi::arch32::stat64 = stat_buffer.try_into().map_err(|_| errno!(EINVAL))?;
2984        // Now we copy to the arch32 version and write.
2985        current_task.write_object(arch32_stat_buf, &result)?;
2986        Ok(())
2987    }
2988
2989    pub fn sys_arch32_fstat64(
2990        current_task: &CurrentTask,
2991        fd: FdNumber,
2992        arch32_stat_buf: UserRef<uapi::arch32::stat64>,
2993    ) -> Result<(), Errno> {
2994        let file = current_task.files().get_allowing_opath(fd)?;
2995        stat64(current_task, file.node(), arch32_stat_buf)
2996    }
2997
2998    pub fn sys_arch32_fallocate(
2999        current_task: &CurrentTask,
3000        fd: FdNumber,
3001        mode: u32,
3002        offset_low: u32,
3003        offset_high: u32,
3004        len_low: u32,
3005        len_high: u32,
3006    ) -> Result<(), Errno> {
3007        let offset = merge_low_and_high(offset_low, offset_high);
3008        let len = merge_low_and_high(len_low, len_high);
3009        sys_fallocate(current_task, fd, mode, offset, len)
3010    }
3011
3012    pub fn sys_arch32_stat64(
3013        current_task: &CurrentTask,
3014        user_path: UserCString,
3015        arch32_stat_buf: UserRef<uapi::arch32::stat64>,
3016    ) -> Result<(), Errno> {
3017        let name = lookup_at(current_task, FdNumber::AT_FDCWD, user_path, LookupFlags::default())?;
3018        stat64(current_task, &name.entry.node, arch32_stat_buf)
3019    }
3020
3021    pub fn sys_arch32_readlink(
3022        current_task: &CurrentTask,
3023        user_path: UserCString,
3024        buffer: UserAddress,
3025        buffer_size: usize,
3026    ) -> Result<usize, Errno> {
3027        sys_readlinkat(current_task, FdNumber::AT_FDCWD, user_path, buffer, buffer_size)
3028    }
3029
3030    pub fn sys_arch32_mkdir(
3031        current_task: &CurrentTask,
3032        user_path: UserCString,
3033        mode: FileMode,
3034    ) -> Result<(), Errno> {
3035        sys_mkdirat(current_task, FdNumber::AT_FDCWD, user_path, mode)
3036    }
3037
3038    pub fn sys_arch32_rmdir(
3039        current_task: &CurrentTask,
3040        user_path: UserCString,
3041    ) -> Result<(), Errno> {
3042        sys_unlinkat(&current_task, FdNumber::AT_FDCWD, user_path, AT_REMOVEDIR)
3043    }
3044
3045    #[allow(non_snake_case)]
3046    pub fn sys_arch32__llseek(
3047        current_task: &CurrentTask,
3048        fd: FdNumber,
3049        offset_high: u32,
3050        offset_low: u32,
3051        result: UserRef<off_t>,
3052        whence: u32,
3053    ) -> Result<(), Errno> {
3054        let offset = merge_low_and_high(offset_low, offset_high);
3055        let result_value = sys_lseek(current_task, fd, offset, whence)?;
3056        current_task.write_object(result, &result_value).map(|_| ())
3057    }
3058
3059    pub fn sys_arch32_dup2(
3060        current_task: &CurrentTask,
3061        oldfd: FdNumber,
3062        newfd: FdNumber,
3063    ) -> Result<FdNumber, Errno> {
3064        if oldfd == newfd {
3065            // O_PATH allowed for:
3066            //
3067            //  Duplicating the file descriptor (dup(2), fcntl(2)
3068            //  F_DUPFD, etc.).
3069            //
3070            // See https://man7.org/linux/man-pages/man2/open.2.html
3071            current_task.files().get_allowing_opath(oldfd)?;
3072            return Ok(newfd);
3073        }
3074        sys_dup3(current_task, oldfd, newfd, 0)
3075    }
3076
3077    pub fn sys_arch32_unlink(
3078        current_task: &CurrentTask,
3079        user_path: UserCString,
3080    ) -> Result<(), Errno> {
3081        sys_unlinkat(&current_task, FdNumber::AT_FDCWD, user_path, 0)
3082    }
3083
3084    pub fn sys_arch32_pread64(
3085        current_task: &CurrentTask,
3086        fd: FdNumber,
3087        address: UserAddress,
3088        length: usize,
3089        _: SyscallArg,
3090        offset_low: u32,
3091        offset_high: u32,
3092    ) -> Result<usize, Errno> {
3093        super::sys_pread64(
3094            current_task,
3095            fd,
3096            address,
3097            length,
3098            merge_low_and_high(offset_low, offset_high),
3099        )
3100    }
3101
3102    pub fn sys_arch32_pwrite64(
3103        current_task: &CurrentTask,
3104        fd: FdNumber,
3105        address: UserAddress,
3106        length: usize,
3107        _: SyscallArg,
3108        offset_low: u32,
3109        offset_high: u32,
3110    ) -> Result<usize, Errno> {
3111        super::sys_pwrite64(
3112            current_task,
3113            fd,
3114            address,
3115            length,
3116            merge_low_and_high(offset_low, offset_high),
3117        )
3118    }
3119
3120    pub fn sys_arch32_truncate64(
3121        current_task: &CurrentTask,
3122        user_path: UserCString,
3123        _unused: SyscallArg,
3124        length_low: u32,
3125        length_high: u32,
3126    ) -> Result<(), Errno> {
3127        super::sys_truncate(current_task, user_path, merge_low_and_high(length_low, length_high))
3128    }
3129
3130    pub fn sys_arch32_ftruncate64(
3131        current_task: &CurrentTask,
3132        fd: FdNumber,
3133        _: SyscallArg,
3134        length_low: u32,
3135        length_high: u32,
3136    ) -> Result<(), Errno> {
3137        super::sys_ftruncate(current_task, fd, merge_low_and_high(length_low, length_high))
3138    }
3139
3140    pub fn sys_arch32_chmod(
3141        current_task: &CurrentTask,
3142        user_path: UserCString,
3143        mode: FileMode,
3144    ) -> Result<(), Errno> {
3145        super::sys_fchmodat(current_task, FdNumber::AT_FDCWD, user_path, mode)
3146    }
3147
3148    pub fn sys_arch32_chown32(
3149        current_task: &CurrentTask,
3150        user_path: UserCString,
3151        owner: uapi::arch32::__kernel_uid32_t,
3152        group: uapi::arch32::__kernel_uid32_t,
3153    ) -> Result<(), Errno> {
3154        super::sys_fchownat(current_task, FdNumber::AT_FDCWD, user_path, owner, group, 0)
3155    }
3156
3157    pub fn sys_arch32_poll(
3158        current_task: &mut CurrentTask,
3159        user_fds: UserRef<uapi::pollfd>,
3160        num_fds: i32,
3161        timeout: i32,
3162    ) -> Result<usize, Errno> {
3163        let deadline = zx::MonotonicInstant::after(duration_from_poll_timeout(timeout)?);
3164        super::poll(current_task, user_fds, num_fds, None, deadline)
3165    }
3166
3167    pub fn sys_arch32_epoll_create(
3168        current_task: &CurrentTask,
3169        size: i32,
3170    ) -> Result<FdNumber, Errno> {
3171        if size < 1 {
3172            // The man page for epoll_create says the size was used in a previous implementation as
3173            // a hint but no longer does anything. But it's still required to be >= 1 to ensure
3174            // programs are backwards-compatible.
3175            return error!(EINVAL);
3176        }
3177        super::sys_epoll_create1(current_task, 0)
3178    }
3179
3180    pub fn sys_arch32_epoll_wait(
3181        current_task: &mut CurrentTask,
3182        epfd: FdNumber,
3183        events: UserRef<EpollEvent>,
3184        max_events: i32,
3185        timeout: i32,
3186    ) -> Result<usize, Errno> {
3187        super::sys_epoll_pwait(
3188            current_task,
3189            epfd,
3190            events,
3191            max_events,
3192            timeout,
3193            UserRef::<SigSet>::default(),
3194        )
3195    }
3196
3197    pub fn sys_arch32_rename(
3198        current_task: &CurrentTask,
3199        old_user_path: UserCString,
3200        new_user_path: UserCString,
3201    ) -> Result<(), Errno> {
3202        super::sys_renameat2(
3203            current_task,
3204            FdNumber::AT_FDCWD,
3205            old_user_path,
3206            FdNumber::AT_FDCWD,
3207            new_user_path,
3208            0,
3209        )
3210    }
3211
3212    pub fn sys_arch32_creat(
3213        current_task: &CurrentTask,
3214        user_path: UserCString,
3215        mode: FileMode,
3216    ) -> Result<FdNumber, Errno> {
3217        super::sys_openat(
3218            current_task,
3219            FdNumber::AT_FDCWD,
3220            user_path,
3221            (OpenFlags::WRONLY | OpenFlags::CREAT | OpenFlags::TRUNC).bits(),
3222            mode,
3223        )
3224    }
3225
3226    pub fn sys_arch32_symlink(
3227        current_task: &CurrentTask,
3228        user_target: UserCString,
3229        user_path: UserCString,
3230    ) -> Result<(), Errno> {
3231        super::sys_symlinkat(current_task, user_target, FdNumber::AT_FDCWD, user_path)
3232    }
3233
3234    pub fn sys_arch32_eventfd(current_task: &CurrentTask, value: u32) -> Result<FdNumber, Errno> {
3235        super::sys_eventfd2(current_task, value, 0)
3236    }
3237
3238    pub fn sys_arch32_link(
3239        current_task: &CurrentTask,
3240        old_user_path: UserCString,
3241        new_user_path: UserCString,
3242    ) -> Result<(), Errno> {
3243        super::sys_linkat(
3244            current_task,
3245            FdNumber::AT_FDCWD,
3246            old_user_path,
3247            FdNumber::AT_FDCWD,
3248            new_user_path,
3249            0,
3250        )
3251    }
3252
3253    pub fn sys_arch32_fstatfs64(
3254        current_task: &CurrentTask,
3255        fd: FdNumber,
3256        user_buf_len: u32,
3257        user_buf: StatFs64Ptr,
3258    ) -> Result<(), Errno> {
3259        if (user_buf_len as usize) < std::mem::size_of::<uapi::arch32::statfs64>() {
3260            return error!(EINVAL);
3261        }
3262        super::fstatfs(current_task, fd, user_buf)
3263    }
3264
3265    pub fn sys_arch32_statfs64(
3266        current_task: &CurrentTask,
3267        user_path: UserCString,
3268        user_buf_len: u32,
3269        user_buf: StatFs64Ptr,
3270    ) -> Result<(), Errno> {
3271        if (user_buf_len as usize) < std::mem::size_of::<uapi::arch32::statfs64>() {
3272            return error!(EINVAL);
3273        }
3274        super::statfs(current_task, user_path, user_buf)
3275    }
3276
3277    pub fn sys_arch32_arm_fadvise64_64(
3278        current_task: &CurrentTask,
3279        fd: FdNumber,
3280        advice: u32,
3281        offset_low: u32,
3282        offset_high: u32,
3283        len_low: u32,
3284        len_high: u32,
3285    ) -> Result<(), Errno> {
3286        let offset = merge_low_and_high(offset_low, offset_high);
3287        let len = merge_low_and_high(len_low, len_high);
3288        super::sys_fadvise64(current_task, fd, offset, len, advice)
3289    }
3290
3291    pub fn sys_arch32_sendfile64(
3292        current_task: &CurrentTask,
3293        out_fd: FdNumber,
3294        in_fd: FdNumber,
3295        user_offset: UserRef<uapi::off_t>,
3296        count: i32,
3297    ) -> Result<usize, Errno> {
3298        super::sys_sendfile(current_task, out_fd, in_fd, user_offset.into(), count)
3299    }
3300
3301    pub use super::{
3302        sys_chdir as sys_arch32_chdir, sys_chroot as sys_arch32_chroot,
3303        sys_copy_file_range as sys_arch32_copy_file_range, sys_dup3 as sys_arch32_dup3,
3304        sys_epoll_create1 as sys_arch32_epoll_create1, sys_epoll_ctl as sys_arch32_epoll_ctl,
3305        sys_epoll_pwait as sys_arch32_epoll_pwait, sys_epoll_pwait2 as sys_arch32_epoll_pwait2,
3306        sys_eventfd2 as sys_arch32_eventfd2, sys_fchmod as sys_arch32_fchmod,
3307        sys_fchmodat as sys_arch32_fchmodat, sys_fchown as sys_arch32_fchown32,
3308        sys_fchown as sys_arch32_fchown, sys_fchownat as sys_arch32_fchownat,
3309        sys_fdatasync as sys_arch32_fdatasync, sys_flock as sys_arch32_flock,
3310        sys_fsetxattr as sys_arch32_fsetxattr, sys_fstatat64 as sys_arch32_fstatat64,
3311        sys_fstatfs as sys_arch32_fstatfs, sys_fsync as sys_arch32_fsync,
3312        sys_ftruncate as sys_arch32_ftruncate, sys_io_cancel as sys_arch32_io_cancel,
3313        sys_io_destroy as sys_arch32_io_destroy, sys_io_getevents as sys_arch32_io_getevents,
3314        sys_io_setup as sys_arch32_io_setup, sys_io_submit as sys_arch32_io_submit,
3315        sys_lgetxattr as sys_arch32_lgetxattr, sys_linkat as sys_arch32_linkat,
3316        sys_listxattr as sys_arch32_listxattr, sys_llistxattr as sys_arch32_llistxattr,
3317        sys_lsetxattr as sys_arch32_lsetxattr, sys_mkdirat as sys_arch32_mkdirat,
3318        sys_mknodat as sys_arch32_mknodat, sys_pidfd_getfd as sys_arch32_pidfd_getfd,
3319        sys_pidfd_open as sys_arch32_pidfd_open, sys_ppoll as sys_arch32_ppoll,
3320        sys_preadv as sys_arch32_preadv, sys_pselect6 as sys_arch32_pselect6,
3321        sys_readv as sys_arch32_readv, sys_removexattr as sys_arch32_removexattr,
3322        sys_renameat2 as sys_arch32_renameat2, sys_select as sys_arch32__newselect,
3323        sys_sendfile as sys_arch32_sendfile, sys_setxattr as sys_arch32_setxattr,
3324        sys_splice as sys_arch32_splice, sys_statfs as sys_arch32_statfs,
3325        sys_statx as sys_arch32_statx, sys_symlinkat as sys_arch32_symlinkat,
3326        sys_sync as sys_arch32_sync, sys_syncfs as sys_arch32_syncfs, sys_tee as sys_arch32_tee,
3327        sys_timerfd_create as sys_arch32_timerfd_create,
3328        sys_timerfd_gettime as sys_arch32_timerfd_gettime,
3329        sys_timerfd_settime as sys_arch32_timerfd_settime, sys_truncate as sys_arch32_truncate,
3330        sys_umask as sys_arch32_umask, sys_utimensat as sys_arch32_utimensat,
3331        sys_vmsplice as sys_arch32_vmsplice,
3332    };
3333}
3334
3335#[cfg(target_arch = "aarch64")]
3336pub use arch32::*;
3337
3338#[cfg(test)]
3339mod tests {
3340    use super::*;
3341    use crate::task::KernelFeatures;
3342    use crate::testing::*;
3343    use starnix_types::vfs::default_statfs;
3344    use starnix_uapi::{O_RDONLY, SEEK_CUR, SEEK_END, SEEK_SET};
3345    use zerocopy::IntoBytes;
3346
3347    #[::fuchsia::test]
3348    async fn test_sys_lseek() -> Result<(), Errno> {
3349        spawn_kernel_and_run_with_pkgfs(async |current_task| {
3350            let fd = FdNumber::from_raw(10);
3351            let file_handle =
3352                current_task.open_file("data/testfile.txt".into(), OpenFlags::RDONLY)?;
3353            let file_size = file_handle.node().stat(current_task).unwrap().st_size;
3354            current_task.files().insert(current_task, fd, file_handle).unwrap();
3355
3356            assert_eq!(sys_lseek(current_task, fd, 0, SEEK_CUR)?, 0);
3357            assert_eq!(sys_lseek(current_task, fd, 1, SEEK_CUR)?, 1);
3358            assert_eq!(sys_lseek(current_task, fd, 3, SEEK_SET)?, 3);
3359            assert_eq!(sys_lseek(current_task, fd, -3, SEEK_CUR)?, 0);
3360            assert_eq!(sys_lseek(current_task, fd, 0, SEEK_END)?, file_size);
3361            assert_eq!(sys_lseek(current_task, fd, -5, SEEK_SET), error!(EINVAL));
3362
3363            // Make sure that the failed call above did not change the offset.
3364            assert_eq!(sys_lseek(current_task, fd, 0, SEEK_CUR)?, file_size);
3365
3366            // Prepare for an overflow.
3367            assert_eq!(sys_lseek(current_task, fd, 3, SEEK_SET)?, 3);
3368
3369            // Check for overflow.
3370            assert_eq!(sys_lseek(current_task, fd, i64::MAX, SEEK_CUR), error!(EINVAL));
3371
3372            Ok(())
3373        })
3374        .await
3375    }
3376
3377    #[::fuchsia::test]
3378    async fn test_sys_dup() -> Result<(), Errno> {
3379        spawn_kernel_and_run_with_pkgfs(async |current_task| {
3380            let file_handle =
3381                current_task.open_file("data/testfile.txt".into(), OpenFlags::RDONLY)?;
3382            let oldfd = current_task.add_file(file_handle, FdFlags::empty())?;
3383            let newfd = sys_dup(current_task, oldfd)?;
3384
3385            assert_ne!(oldfd, newfd);
3386            let files = current_task.files();
3387            assert!(Arc::ptr_eq(&files.get(oldfd).unwrap(), &files.get(newfd).unwrap()));
3388
3389            assert_eq!(sys_dup(current_task, FdNumber::from_raw(3)), error!(EBADF));
3390
3391            Ok(())
3392        })
3393        .await
3394    }
3395
3396    #[::fuchsia::test]
3397    async fn test_sys_dup3() -> Result<(), Errno> {
3398        spawn_kernel_and_run_with_pkgfs(async |current_task| {
3399            let file_handle =
3400                current_task.open_file("data/testfile.txt".into(), OpenFlags::RDONLY)?;
3401            let oldfd = current_task.add_file(file_handle, FdFlags::empty())?;
3402            let newfd = FdNumber::from_raw(2);
3403            sys_dup3(current_task, oldfd, newfd, O_CLOEXEC)?;
3404
3405            assert_ne!(oldfd, newfd);
3406            let files = current_task.files();
3407            assert!(Arc::ptr_eq(&files.get(oldfd).unwrap(), &files.get(newfd).unwrap()));
3408            assert_eq!(files.get_fd_flags_allowing_opath(oldfd).unwrap(), FdFlags::empty());
3409            assert_eq!(files.get_fd_flags_allowing_opath(newfd).unwrap(), FdFlags::CLOEXEC);
3410
3411            assert_eq!(sys_dup3(current_task, oldfd, oldfd, O_CLOEXEC), error!(EINVAL));
3412
3413            // Pass invalid flags.
3414            let invalid_flags = 1234;
3415            assert_eq!(sys_dup3(current_task, oldfd, newfd, invalid_flags), error!(EINVAL));
3416
3417            // Makes sure that dup closes the old file handle before the fd points
3418            // to the new file handle.
3419            let second_file_handle =
3420                current_task.open_file("data/testfile.txt".into(), OpenFlags::RDONLY)?;
3421            let different_file_fd = current_task.add_file(second_file_handle, FdFlags::empty())?;
3422            assert!(!Arc::ptr_eq(
3423                &files.get(oldfd).unwrap(),
3424                &files.get(different_file_fd).unwrap()
3425            ));
3426            sys_dup3(current_task, oldfd, different_file_fd, O_CLOEXEC)?;
3427            assert!(Arc::ptr_eq(
3428                &files.get(oldfd).unwrap(),
3429                &files.get(different_file_fd).unwrap()
3430            ));
3431
3432            Ok(())
3433        })
3434        .await
3435    }
3436
3437    #[::fuchsia::test]
3438    async fn test_sys_open_cloexec() -> Result<(), Errno> {
3439        spawn_kernel_and_run_with_pkgfs(async |current_task| {
3440            let path_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
3441            let path = b"data/testfile.txt\0";
3442            current_task.write_memory(path_addr, path)?;
3443            let fd = sys_openat(
3444                &current_task,
3445                FdNumber::AT_FDCWD,
3446                UserCString::new(current_task, path_addr),
3447                O_RDONLY | O_CLOEXEC,
3448                FileMode::default(),
3449            )?;
3450            assert!(
3451                current_task.files().get_fd_flags_allowing_opath(fd)?.contains(FdFlags::CLOEXEC)
3452            );
3453            Ok(())
3454        })
3455        .await
3456    }
3457
3458    #[::fuchsia::test]
3459    async fn test_sys_epoll() -> Result<(), Errno> {
3460        spawn_kernel_and_run_with_pkgfs(async |current_task| {
3461            let epoll_fd = sys_epoll_create1(current_task, 0).expect("sys_epoll_create1 failed");
3462            sys_close(current_task, epoll_fd).expect("sys_close failed");
3463
3464            Ok(())
3465        })
3466        .await
3467    }
3468
3469    #[::fuchsia::test]
3470    async fn test_fstat_tmp_file() {
3471        spawn_kernel_and_run(async |current_task| {
3472            // Create the file that will be used to stat.
3473            let file_path = "testfile.txt";
3474            let _file_handle = current_task
3475                .open_file_at(
3476                    FdNumber::AT_FDCWD,
3477                    file_path.into(),
3478                    OpenFlags::RDWR | OpenFlags::CREAT,
3479                    FileMode::ALLOW_ALL,
3480                    ResolveFlags::empty(),
3481                    AccessCheck::default(),
3482                )
3483                .unwrap();
3484
3485            // Write the path to user memory.
3486            let path_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
3487            current_task
3488                .write_memory(path_addr, file_path.as_bytes())
3489                .expect("failed to clear struct");
3490
3491            let memory_len = (path_addr + file_path.len()).expect("OOB memory allocation!");
3492            let user_stat = UserRef::new(memory_len);
3493            current_task
3494                .write_object(user_stat, &default_statfs(0))
3495                .expect("failed to clear struct");
3496
3497            let user_path = UserCString::new(current_task, path_addr);
3498
3499            assert_eq!(sys_statfs(current_task, user_path, user_stat.into()), Ok(()));
3500
3501            let returned_stat = current_task.read_object(user_stat).expect("failed to read struct");
3502            let expected_stat = starnix_uapi::statfs {
3503                f_blocks: 0x100000000,
3504                f_bavail: 0x100000000,
3505                f_bfree: 0x100000000,
3506                f_flags: starnix_uapi::MS_RELATIME as i64,
3507                ..default_statfs(starnix_uapi::TMPFS_MAGIC)
3508            };
3509            assert!(
3510                returned_stat.as_bytes() == expected_stat.as_bytes(),
3511                "Expected {:?}, got {:?}",
3512                expected_stat,
3513                returned_stat
3514            );
3515        })
3516        .await;
3517    }
3518
3519    #[::fuchsia::test]
3520    async fn test_unlinkat_dir() {
3521        spawn_kernel_and_run(async |current_task| {
3522            // Create the dir that we will attempt to unlink later.
3523            let no_slash_path = b"testdir";
3524            let no_slash_path_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
3525            current_task
3526                .write_memory(no_slash_path_addr, no_slash_path)
3527                .expect("failed to write path");
3528            let no_slash_user_path = UserCString::new(current_task, no_slash_path_addr);
3529            sys_mkdirat(
3530                &current_task,
3531                FdNumber::AT_FDCWD,
3532                no_slash_user_path,
3533                FileMode::ALLOW_ALL.with_type(FileMode::IFDIR),
3534            )
3535            .unwrap();
3536
3537            let slash_path = b"testdir/";
3538            let slash_path_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
3539            current_task.write_memory(slash_path_addr, slash_path).expect("failed to write path");
3540            let slash_user_path = UserCString::new(current_task, slash_path_addr);
3541
3542            // Try to remove a directory without specifying AT_REMOVEDIR.
3543            // This should fail with EISDIR, irrespective of the terminating slash.
3544            let error =
3545                sys_unlinkat(&current_task, FdNumber::AT_FDCWD, slash_user_path, 0).unwrap_err();
3546            assert_eq!(error, errno!(EISDIR));
3547            let error =
3548                sys_unlinkat(&current_task, FdNumber::AT_FDCWD, no_slash_user_path, 0).unwrap_err();
3549            assert_eq!(error, errno!(EISDIR));
3550
3551            // Success with AT_REMOVEDIR.
3552            sys_unlinkat(&current_task, FdNumber::AT_FDCWD, slash_user_path, AT_REMOVEDIR).unwrap();
3553        })
3554        .await;
3555    }
3556
3557    #[::fuchsia::test]
3558    async fn test_rename_noreplace() {
3559        spawn_kernel_and_run(async |current_task| {
3560            // Create the file that will be renamed.
3561            let old_user_path = "testfile.txt";
3562            let _old_file_handle = current_task
3563                .open_file_at(
3564                    FdNumber::AT_FDCWD,
3565                    old_user_path.into(),
3566                    OpenFlags::RDWR | OpenFlags::CREAT,
3567                    FileMode::ALLOW_ALL,
3568                    ResolveFlags::empty(),
3569                    AccessCheck::default(),
3570                )
3571                .unwrap();
3572
3573            // Write the path to user memory.
3574            let old_path_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
3575            current_task
3576                .write_memory(old_path_addr, old_user_path.as_bytes())
3577                .expect("failed to clear struct");
3578
3579            // Create a second file that we will attempt to rename to.
3580            let new_user_path = "testfile2.txt";
3581            let _new_file_handle = current_task
3582                .open_file_at(
3583                    FdNumber::AT_FDCWD,
3584                    new_user_path.into(),
3585                    OpenFlags::RDWR | OpenFlags::CREAT,
3586                    FileMode::ALLOW_ALL,
3587                    ResolveFlags::empty(),
3588                    AccessCheck::default(),
3589                )
3590                .unwrap();
3591
3592            // Write the path to user memory.
3593            let new_path_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
3594            current_task
3595                .write_memory(new_path_addr, new_user_path.as_bytes())
3596                .expect("failed to clear struct");
3597
3598            // Try to rename first file to second file's name with RENAME_NOREPLACE flag.
3599            // This should fail with EEXIST.
3600            let error = sys_renameat2(
3601                &current_task,
3602                FdNumber::AT_FDCWD,
3603                UserCString::new(current_task, old_path_addr),
3604                FdNumber::AT_FDCWD,
3605                UserCString::new(current_task, new_path_addr),
3606                RenameFlags::NOREPLACE.bits(),
3607            )
3608            .unwrap_err();
3609            assert_eq!(error, errno!(EEXIST));
3610        })
3611        .await;
3612    }
3613
3614    #[::fuchsia::test]
3615    async fn test_sys_sync() -> Result<(), Errno> {
3616        spawn_kernel_and_run(async |current_task| {
3617            sys_sync(current_task)?;
3618            Ok(())
3619        })
3620        .await
3621    }
3622
3623    #[::fuchsia::test]
3624    async fn test_sys_syncfs() -> Result<(), Errno> {
3625        spawn_kernel_and_run(async |current_task| {
3626            let file_handle = current_task.open_file(".".into(), OpenFlags::RDONLY)?;
3627            let fd = current_task.add_file(file_handle, FdFlags::empty())?;
3628            sys_syncfs(current_task, fd)?;
3629            Ok(())
3630        })
3631        .await
3632    }
3633
3634    // TODO(https://fxbug.dev/485370648) remove when unnecessary
3635    #[::fuchsia::test]
3636    async fn test_fake_ion_stat() {
3637        // Test with fake_ion disabled (default).
3638        spawn_kernel_and_run(async |current_task| {
3639            let ion_path = b"/dev/ion\0";
3640            let path_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
3641            current_task.write_memory(path_addr, ion_path).expect("failed to write path");
3642            let user_path = UserCString::new(current_task, path_addr);
3643
3644            let stat_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
3645            let stat_ptr = StatPtr::new(current_task, stat_addr);
3646
3647            let error = sys_fstatat64(&current_task, FdNumber::AT_FDCWD, user_path, stat_ptr, 0)
3648                .unwrap_err();
3649            assert_eq!(error, errno!(ENOENT));
3650        })
3651        .await;
3652
3653        // Test with fake_ion enabled.
3654        let mut features = KernelFeatures::default();
3655        features.fake_ion = true;
3656        spawn_kernel_with_features_and_run(
3657            async |current_task| {
3658                let ion_path = b"/dev/ion\0";
3659                let path_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
3660                current_task.write_memory(path_addr, ion_path).expect("failed to write path");
3661                let user_path = UserCString::new(current_task, path_addr);
3662
3663                let stat_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
3664                let stat_ptr = StatPtr::new(current_task, stat_addr);
3665
3666                sys_fstatat64(&current_task, FdNumber::AT_FDCWD, user_path, stat_ptr, 0)
3667                    .expect("sys_fstatat64 should succeed with fake_ion");
3668
3669                let stat_result: uapi::stat =
3670                    current_task.read_object(stat_addr.into()).expect("failed to read stat");
3671                assert_eq!(stat_result.st_mode, uapi::S_IFCHR | 0o666);
3672                assert_eq!(stat_result.st_rdev, DeviceId::new(10, 59).bits());
3673
3674                // Test statx as well.
3675                let statx_addr = map_memory(&current_task, UserAddress::default(), *PAGE_SIZE);
3676                let statx_ptr = UserRef::new(statx_addr);
3677                sys_statx(
3678                    &current_task,
3679                    FdNumber::AT_FDCWD,
3680                    user_path,
3681                    0,
3682                    uapi::STATX_BASIC_STATS,
3683                    statx_ptr,
3684                )
3685                .expect("sys_statx should succeed with fake_ion");
3686
3687                let statx_result: statx =
3688                    current_task.read_object(statx_ptr).expect("failed to read statx");
3689                assert_eq!(statx_result.stx_mode, (uapi::S_IFCHR | 0o666) as u16);
3690                assert_eq!(statx_result.stx_rdev_major, 10);
3691                assert_eq!(statx_result.stx_rdev_minor, 59);
3692            },
3693            features,
3694        )
3695        .await;
3696    }
3697}