Skip to main content

starnix_core/vfs/
syscalls.rs

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