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