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