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