Skip to main content

starnix_core/vfs/
syscalls.rs

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