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