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