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