1use core::{
2 convert::TryFrom,
3 mem::{self, size_of},
4 num::NonZeroU64,
5 ptr, slice, str,
6};
7use object::bytes_of_slice_mut;
8use redox_protocols::protocol::{WaitFlags, wifstopped};
9use redox_rt::{
10 RtTcb,
11 sys::{Resugid, WaitpidTarget},
12};
13use syscall::{
14 self, EILSEQ, ESRCH, Error, MODE_PERM, StdFsCallKind, StdFsCallMeta,
15 data::{Map, TimeSpec as redox_timespec},
16 dirent::DirentHeader,
17};
18
19use self::{
20 exec::Executable,
21 path::{FileLock, canonicalize, openat2, openat2_path},
22};
23use super::{Pal, Read, types::*};
24use crate::{
25 c_str::{CStr, CString},
26 error::{Errno, Result},
27 fs::File,
28 header::{
29 errno::{
30 EBADF, EBADFD, EEXIST, EFAULT, EFBIG, EINTR, EINVAL, EIO, EMFILE, ENAMETOOLONG, ENOENT,
31 ENOEXEC, ENOMEM, ENOSYS, EOPNOTSUPP, EPERM,
32 },
33 fcntl::{
34 self, AT_EACCESS, AT_EMPTY_PATH, AT_FDCWD, AT_REMOVEDIR, AT_SYMLINK_FOLLOW, F_GETLK,
35 F_OFD_GETLK, F_OFD_SETLK, F_RDLCK, F_SETLK, F_SETLKW, F_UNLCK, F_WRLCK, flock,
36 },
37 limits::{self},
38 pthread::{pthread_cancel, pthread_create},
39 signal::{NSIG, SIGEV_NONE, SIGEV_SIGNAL, SIGEV_THREAD, SIGRTMIN, sigevent},
40 stdio::RENAME_NOREPLACE,
41 stdlib::posix_memalign,
42 sys_file,
43 sys_mman::{MAP_ANONYMOUS, PROT_READ, PROT_WRITE},
44 sys_random,
45 sys_resource::{PRIO_PROCESS, RLIM_INFINITY, rlimit, rusage, setpriority},
46 sys_select::timeval,
47 sys_stat::{S_ISGID, S_ISUID, S_ISVTX, stat},
48 sys_statvfs::statvfs,
49 sys_time::timezone,
50 sys_utsname::{UTSLENGTH, utsname},
51 time::{
52 CLOCK_MONOTONIC, CLOCK_REALTIME, TIMER_ABSTIME, itimerspec, timer_internal_t, timespec,
53 },
54 unistd::{F_OK, R_OK, SEEK_CUR, SEEK_SET, W_OK, X_OK},
55 },
56 io::{self, BufReader, prelude::*},
57 iter::NulTerminated,
58 ld_so::tcb::OsSpecific,
59 out::Out,
60 platform::{
61 ERRNO, free,
62 sys::{
63 path::{CwdPath, to_cwd_path},
64 timer::{TIMERS, timer_routine, timer_update_wake_time},
65 },
66 },
67 sync::rwlock::RwLock,
68};
69
70pub use redox_rt::proc::FdGuard;
71
72mod epoll;
73mod event;
74pub(crate) mod exec;
75mod extra;
76mod libcscheme;
77mod libredox;
78pub(crate) mod path;
79mod ptrace;
80pub(crate) mod signal;
81mod socket;
82mod timer;
83
84static mut BRK_CUR: *mut c_void = ptr::null_mut();
85static mut BRK_END: *mut c_void = ptr::null_mut();
86
87const PAGE_SIZE: usize = 4096;
88fn round_up_to_page_size(val: usize) -> Option<usize> {
89 val.checked_add(PAGE_SIZE)
90 .map(|val| (val - 1) / PAGE_SIZE * PAGE_SIZE)
91}
92
93fn cvt_uid(id: c_int) -> Result<Option<u32>> {
94 if id == -1 {
95 return Ok(None);
96 }
97 Ok(Some(id.try_into().map_err(|_| Errno(EINVAL))?))
98}
99
100static CLONE_LOCK: RwLock<()> = RwLock::new(());
101
102pub struct Sys;
104
105impl Pal for Sys {
106 fn faccessat(fd: c_int, path: CStr, mode: c_int, flags: c_int) -> Result<()> {
107 let fd = FdGuard::new(Sys::openat(fd, path, fcntl::O_PATH | fcntl::O_CLOEXEC, 0)? as usize);
108
109 if (flags & !(AT_EACCESS)) != 0 {
110 return Err(Errno(EINVAL));
111 }
112
113 if mode == F_OK {
114 return Ok(());
115 }
116
117 let mut stat = syscall::Stat::default();
118
119 fd.fstat(&mut stat)?;
120
121 let Resugid {
122 ruid,
123 rgid,
124 euid,
125 egid,
126 ..
127 } = redox_rt::sys::posix_getresugid();
128 let (uid, gid) = if (flags & AT_EACCESS) == AT_EACCESS {
129 (euid, egid)
130 } else {
131 (ruid, rgid)
132 };
133
134 let perms = (if stat.st_uid == uid {
135 stat.st_mode >> (3 * 2)
136 } else if stat.st_gid == gid {
137 stat.st_mode >> (3 * 1)
138 } else {
139 stat.st_mode
140 }) & 0o7;
141 if (mode & R_OK == R_OK && perms & 0o4 != 0o4)
142 || (mode & W_OK == W_OK && perms & 0o2 != 0o2)
143 || (mode & X_OK == X_OK && perms & 0o1 != 0o1)
144 {
145 return Err(Errno(EINVAL));
146 }
147
148 Ok(())
149 }
150
151 unsafe fn brk(addr: *mut c_void) -> Result<*mut c_void> {
152 if unsafe { BRK_CUR }.is_null() {
154 const BRK_MAX_SIZE: usize = 4 * 1024 * 1024;
156
157 let allocated = unsafe {
158 Self::mmap(
159 ptr::null_mut(),
160 BRK_MAX_SIZE,
161 PROT_READ | PROT_WRITE,
162 MAP_ANONYMOUS,
163 0,
164 0,
165 )
166 }?;
167
168 unsafe {
169 BRK_CUR = allocated;
170 BRK_END = (allocated as *mut u8).add(BRK_MAX_SIZE) as *mut c_void
171 };
172 }
173
174 if addr.is_null() {
175 Ok(unsafe { BRK_CUR })
177 } else if unsafe { BRK_CUR } <= addr && addr < unsafe { BRK_END } {
178 unsafe { BRK_CUR = addr };
180 Ok(addr)
181 } else {
182 Err(Errno(ENOMEM))
184 }
185 }
186
187 fn chdir(path: CStr) -> Result<()> {
188 let path = path.to_str().map_err(|_| Errno(EINVAL))?;
189 path::chdir(path)?;
190 Ok(())
191 }
192
193 fn chmod(path: CStr, mode: mode_t) -> Result<()> {
194 let file = File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC)?;
195 Self::fchmod(*file, mode)
196 }
197
198 fn chown(path: CStr, owner: uid_t, group: gid_t) -> Result<()> {
199 let file = File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC)?;
200 Self::fchown(*file, owner, group)
201 }
202
203 fn clock_getres(clk_id: clockid_t, res: Option<Out<timespec>>) -> Result<()> {
204 let path = match clk_id {
205 CLOCK_REALTIME => "/scheme/time/1/getres",
206 CLOCK_MONOTONIC => "/scheme/time/4/getres",
207 _ => return Err(Errno(EINVAL)),
208 };
209 let timerfd = FdGuard::open(&path, syscall::O_RDONLY)?;
210 let mut redox_res = timespec::default();
211 let buffer = unsafe {
212 slice::from_raw_parts_mut(
213 &mut redox_res as *mut _ as *mut u8,
214 mem::size_of::<timespec>(),
215 )
216 };
217
218 let bytes_read = redox_rt::sys::posix_read(timerfd.as_raw_fd(), buffer)?;
219
220 if bytes_read < mem::size_of::<timespec>() {
221 return Err(Errno(EIO));
222 }
223
224 if let Some(mut res) = res {
225 res.write(redox_res);
226 }
227
228 Ok(())
229 }
230
231 fn clock_gettime(clk_id: clockid_t, tp: Out<timespec>) -> Result<()> {
232 libredox::clock_gettime(clk_id as usize, tp)?;
233 Ok(())
234 }
235
236 unsafe fn clock_settime(clk_id: clockid_t, tp: *const timespec) -> Result<()> {
237 todo_skip!(0, "clock_settime({}, {:p}): not implemented", clk_id, tp);
238 Err(Errno(ENOSYS))
239 }
240
241 fn close(fd: c_int) -> Result<()> {
242 redox_rt::sys::close(fd as usize)?;
243 Ok(())
244 }
245
246 fn dup2(fd1: c_int, fd2: c_int) -> Result<c_int> {
247 Ok(redox_rt::sys::dup2(fd1 as usize, fd2 as usize, &[])? as c_int)
248 }
249
250 fn exit(status: c_int) -> ! {
251 let _ = redox_rt::sys::posix_exit(status);
252 loop {}
253 }
254
255 unsafe fn execve(path: CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> Result<()> {
256 self::exec::execve(
257 Executable::AtPath(path),
258 self::exec::ArgEnv::C { argv, envp },
259 None,
260 )?;
261 unreachable!()
262 }
263 unsafe fn fexecve(
264 fildes: c_int,
265 argv: *const *mut c_char,
266 envp: *const *mut c_char,
267 ) -> Result<()> {
268 self::exec::execve(
269 Executable::InFd {
270 file: File::new(fildes),
271 arg0: unsafe { CStr::from_ptr(argv.read()) }.to_bytes(),
272 },
273 self::exec::ArgEnv::C { argv, envp },
274 None,
275 )?;
276 unreachable!()
277 }
278
279 fn fchdir(fd: c_int) -> Result<()> {
280 path::fchdir(fd)?;
281 Ok(())
282 }
283
284 fn fchmodat(dirfd: c_int, path: Option<CStr>, mode: mode_t, flags: c_int) -> Result<()> {
285 const MASK: c_int = !(fcntl::AT_SYMLINK_NOFOLLOW | fcntl::AT_EMPTY_PATH);
286 if MASK & flags != 0 {
287 return Err(Errno(EINVAL));
288 }
289 let mut path = path
290 .and_then(|cs| str::from_utf8(cs.to_bytes()).ok())
291 .ok_or(Errno(ENOENT))?;
292
293 if path.is_empty() {
294 if flags & AT_EMPTY_PATH == AT_EMPTY_PATH {
295 if dirfd == AT_FDCWD {
296 path = ".";
297 } else {
298 return Ok(libredox::fchmod(dirfd as usize, mode as u16)?);
299 }
300 } else {
301 return Err(Errno(ENOENT));
303 }
304 }
305
306 let file = openat2(dirfd, path, flags, 0)?;
307 libredox::fchmod(*file as usize, mode as u16)?;
308 Ok(())
309 }
310
311 fn fchownat(fildes: c_int, path: CStr, owner: uid_t, group: gid_t, flags: c_int) -> Result<()> {
312 const MASK: c_int = !(fcntl::AT_SYMLINK_NOFOLLOW | fcntl::AT_EMPTY_PATH);
313 if MASK & flags != 0 {
314 return Err(Errno(EINVAL));
315 }
316 let mut path = path.to_str().map_err(|_| Errno(ENOENT))?;
317 if path.is_empty() {
318 if flags & AT_EMPTY_PATH == AT_EMPTY_PATH {
319 if fildes == AT_FDCWD {
320 path = ".";
321 } else {
322 return Ok(libredox::fchown(fildes as usize, owner as _, group as _)?);
323 }
324 } else {
325 return Err(Errno(ENOENT));
327 }
328 }
329 let file = openat2(fildes, path, flags, 0)?;
330 libredox::fchown(*file as usize, owner as _, group as _)?;
331 Ok(())
332 }
333
334 fn fcntl(fd: c_int, cmd: c_int, args: c_ulonglong) -> Result<c_int> {
335 match cmd {
336 F_SETLK | F_OFD_SETLK => {
337 let is_ofd = cmd == F_OFD_SETLK;
338 let flock = unsafe { &mut *(args as *mut flock) };
339
340 let (start, len) = Self::relative_to_absolute_foffset(
341 fd as usize,
342 flock.l_whence,
343 flock.l_start,
344 flock.l_len,
345 )?;
346
347 let start = start as u64 | if is_ofd { 1 << 63 } else { 0 };
348 let len = len as u64;
349
350 match flock.l_type as i32 {
351 F_UNLCK => {
352 let meta = StdFsCallMeta::new(StdFsCallKind::Unlock, start, len);
353 syscall::std_fs_call(fd as usize, &mut [], &meta)?;
354 return Ok(0);
355 }
356
357 F_RDLCK | F_WRLCK => {
358 let meta = StdFsCallMeta::new(
359 StdFsCallKind::Lock,
360 start,
361 len | if flock.l_type as i32 == F_WRLCK {
362 1 << 63
363 } else {
364 0
365 },
366 );
367 syscall::std_fs_call(fd as usize, &mut [], &meta)?;
368 return Ok(0);
369 }
370
371 _ => return Err(Errno(EINVAL)),
372 };
373 }
374
375 F_GETLK | F_OFD_GETLK => {
376 let is_ofd = cmd == F_OFD_GETLK;
377 let flock = unsafe { &mut *(args as *mut flock) };
378
379 if is_ofd && flock.l_pid != 0 {
380 log::warn!("POSIX requires `l_pid` to be 0 on input for `F_OFD_GETLK`");
381 return Err(Errno(EINVAL));
382 }
383
384 let (start, len) = Self::relative_to_absolute_foffset(
385 fd as usize,
386 flock.l_whence,
387 flock.l_start,
388 flock.l_len,
389 )?;
390
391 let mut start = start as u64;
392 if is_ofd {
393 start |= 1 << 63;
394 }
395
396 let mut len = len as u64;
397 if flock.l_type as i32 == F_WRLCK {
398 len |= 1 << 63;
399 }
400
401 let meta = StdFsCallMeta::new(StdFsCallKind::GetLock, 0, 0);
402 let payload = &mut [start, len];
403 let val =
405 match syscall::std_fs_call(fd as usize, bytes_of_slice_mut(payload), &meta) {
406 Err(err) if err.errno == ESRCH => {
411 flock.l_type = F_UNLCK as i16;
412 return Ok(0);
413 }
414
415 Ok(val) => val,
416 Err(err) => return Err(Errno(err.errno)),
417 };
418
419 debug_assert_ne!(payload[0] & (1 << 63), 1 << 63);
420
421 if is_ofd {
422 flock.l_pid = -1;
423 } else {
424 flock.l_pid = val as i32;
425 }
426
427 let len = payload[1] & !(1 << 63);
428 if payload[1] & (1 << 63) == (1 << 63) {
429 flock.l_type = F_WRLCK as i16;
430 } else {
431 flock.l_type = F_RDLCK as i16;
432 }
433
434 flock.l_whence = SEEK_SET as _;
435 flock.l_start = start as i64;
436 flock.l_len = len as i64;
437 return Ok(0);
438 }
439
440 F_SETLKW => log::warn!("F_SETLKW: not yet implemented"),
441
442 _ => {}
443 }
444
445 Ok(redox_rt::sys::fcntl(fd as usize, cmd as usize, args as usize)? as c_int)
446 }
447
448 fn fdatasync(fd: c_int) -> Result<()> {
449 syscall::fsync(fd as usize)?;
451 Ok(())
452 }
453
454 fn flock(_fd: c_int, _operation: c_int) -> Result<()> {
455 Ok(())
457 }
458
459 unsafe fn fork() -> Result<pid_t> {
460 let _guard = CLONE_LOCK.write();
462
463 Ok(redox_rt::proc::fork_impl(&redox_rt::proc::ForkArgs::Managed)? as pid_t)
464 }
465
466 fn fstatat(dirfd: c_int, path: Option<CStr>, mut buf: Out<stat>, flags: c_int) -> Result<()> {
467 let path = path.ok_or(Errno(EFAULT))?;
469 let mut path = str::from_utf8(path.to_bytes()).ok().ok_or(Errno(EILSEQ))?;
470
471 if path.is_empty() {
472 if flags & AT_EMPTY_PATH == AT_EMPTY_PATH {
473 if dirfd == AT_FDCWD {
474 path = ".";
475 } else {
476 return Ok(unsafe { libredox::fstat(dirfd as usize, buf.as_mut_ptr()) }?);
477 }
478 } else {
479 return Err(Errno(ENOENT));
481 }
482 }
483
484 let file = openat2(dirfd, path, flags, fcntl::O_PATH)?;
485 let fstat_res = unsafe { libredox::fstat(*file as usize, buf.as_mut_ptr()) };
487 let close_res = redox_rt::sys::close(*file as usize);
488 if let Err(err) = fstat_res {
489 return Err(err.into());
490 }
491 close_res?;
492 Ok(fstat_res?)
493 }
494
495 fn fstatvfs(fildes: c_int, mut buf: Out<statvfs>) -> Result<()> {
496 unsafe {
497 libredox::fstatvfs(fildes as usize, buf.as_mut_ptr())?;
498 }
499 Ok(())
500 }
501
502 fn fsync(fd: c_int) -> Result<()> {
503 libredox::fsync(fd as usize)?;
504 Ok(())
505 }
506
507 fn ftruncate(fd: c_int, len: off_t) -> Result<()> {
508 libredox::ftruncate(fd as usize, len as usize)?;
509 Ok(())
510 }
511
512 #[inline]
513 unsafe fn futex_wait(addr: *mut u32, val: u32, deadline: Option<×pec>) -> Result<()> {
514 let deadline = deadline.map(|d| syscall::TimeSpec::from(d));
515 (unsafe { redox_rt::sys::sys_futex_wait(addr, val, deadline.as_ref()) })?;
516 Ok(())
517 }
518 #[inline]
519 unsafe fn futex_wake(addr: *mut u32, num: u32) -> Result<u32> {
520 Ok(unsafe { redox_rt::sys::sys_futex_wake(addr, num) }?)
521 }
522
523 unsafe fn utimensat(
524 dirfd: c_int,
525 path: CStr,
526 times: *const timespec,
527 flag: c_int,
528 ) -> Result<()> {
529 let mut path = path.to_str().map_err(|_| Errno(ENOENT))?;
530 if path.is_empty() {
531 if flag & AT_EMPTY_PATH == AT_EMPTY_PATH {
532 if dirfd == AT_FDCWD {
533 path = ".";
534 } else {
535 return Ok(unsafe { libredox::futimens(dirfd as usize, times) }?);
536 }
537 } else {
538 return Err(Errno(ENOENT));
540 }
541 }
542
543 let file = openat2(dirfd, path, flag, fcntl::O_PATH | fcntl::O_CLOEXEC)?;
544 Ok(unsafe { libredox::futimens(*file as usize, times) }?)
545 }
546
547 fn getcwd(buf: Out<[u8]>) -> Result<()> {
548 path::getcwd(buf)?;
549 Ok(())
550 }
551
552 fn getdents(fd: c_int, buf: &mut [u8], opaque: u64) -> Result<usize> {
553 Ok(libredox::getdents(fd as usize, buf, opaque)?)
554 }
555
556 fn dir_seek(_fd: c_int, _off: u64) -> Result<()> {
557 Ok(())
559 }
560 unsafe fn dent_reclen_offset(this_dent: &[u8], offset: usize) -> Option<(u16, u64)> {
562 let mut header = DirentHeader::default();
563 header.copy_from_slice(&this_dent.get(..size_of::<DirentHeader>())?);
564
565 if this_dent.get(usize::from(header.record_len) - 1) != Some(&b'\0') {
567 return None;
568 }
569
570 Some((header.record_len, header.next_opaque_id))
571 }
572
573 fn getegid() -> gid_t {
574 redox_rt::sys::posix_getresugid().egid as gid_t
575 }
576
577 fn geteuid() -> uid_t {
578 redox_rt::sys::posix_getresugid().euid as uid_t
579 }
580
581 fn getgid() -> gid_t {
582 redox_rt::sys::posix_getresugid().rgid as gid_t
583 }
584
585 fn getgroups(mut list: Out<[gid_t]>) -> Result<c_int> {
586 let uid = Self::geteuid();
589 let pwd = crate::header::pwd::getpwuid(uid);
590
591 if pwd.is_null() {
592 return Err(Errno(ENOENT));
593 }
594
595 let username = unsafe { CStr::from_ptr((*pwd).pw_name) };
596 let username = username.to_bytes_with_nul();
597 let mut count = 0;
598
599 unsafe {
600 use crate::header::grp;
601 grp::setgrent();
602
603 while let Some(grp) = grp::getgrent().as_ref() {
604 let mut i = 0;
605 let mut found = false;
606
607 while !(*grp.gr_mem.offset(i)).is_null() {
608 let member = CStr::from_ptr(*grp.gr_mem.offset(i));
609 if member.to_bytes_with_nul() == username {
610 found = true;
611 break;
612 }
613 i += 1;
614 }
615
616 if found {
617 if !list.is_empty() && (count as usize) < list.len() {
618 list.index(count).write(grp.gr_gid);
619 }
620 count += 1;
621 }
622 }
623 grp::endgrent();
624 }
625
626 if !list.is_empty() && (count as usize) > list.len() {
627 return Err(Errno(EINVAL));
628 }
629
630 Ok(count as i32)
631 }
632
633 fn getpagesize() -> usize {
634 PAGE_SIZE
635 }
636
637 fn getpgid(pid: pid_t) -> Result<pid_t> {
638 Ok(redox_rt::sys::posix_getpgid(pid as usize)? as pid_t)
639 }
640
641 fn getpid() -> pid_t {
642 redox_rt::sys::posix_getpid() as pid_t
643 }
644
645 fn getppid() -> pid_t {
646 redox_rt::sys::posix_getppid() as pid_t
647 }
648
649 fn getpriority(which: c_int, who: id_t) -> Result<c_int> {
650 match redox_rt::sys::posix_getpriority(which, who as u32) {
651 Ok(kernel_prio) => {
652 let posix_prio = (kernel_prio as i32 * -1) + 40 as i32;
653 Ok(posix_prio)
654 }
655 Err(e) => Err(Errno(e.errno)),
656 }
657 }
658
659 fn getrandom(buf: &mut [u8], flags: c_uint) -> Result<usize> {
660 let path = if flags & sys_random::GRND_RANDOM != 0 {
661 "/scheme/rand"
663 } else {
664 "/scheme/rand"
665 };
666
667 let mut open_flags = syscall::O_RDONLY | redox_protocols::protocol::O_CLOEXEC;
668 if flags & sys_random::GRND_NONBLOCK != 0 {
669 open_flags |= syscall::O_NONBLOCK;
670 }
671
672 let fd = FdGuard::open(path, open_flags)?;
674 Ok(fd.read(buf)?)
675 }
676
677 fn getresgid(
678 rgid_out: Option<Out<gid_t>>,
679 egid_out: Option<Out<gid_t>>,
680 sgid_out: Option<Out<gid_t>>,
681 ) -> Result<()> {
682 let Resugid {
683 rgid, egid, sgid, ..
684 } = redox_rt::sys::posix_getresugid();
685 if let Some(mut rgid_out) = rgid_out {
686 rgid_out.write(rgid as _);
687 }
688 if let Some(mut egid_out) = egid_out {
689 egid_out.write(egid as _);
690 }
691 if let Some(mut sgid_out) = sgid_out {
692 sgid_out.write(sgid as _);
693 }
694 Ok(())
695 }
696 fn getresuid(
697 ruid_out: Option<Out<uid_t>>,
698 euid_out: Option<Out<uid_t>>,
699 suid_out: Option<Out<uid_t>>,
700 ) -> Result<()> {
701 let Resugid {
702 ruid, euid, suid, ..
703 } = redox_rt::sys::posix_getresugid();
704 if let Some(mut ruid_out) = ruid_out {
705 ruid_out.write(ruid as _);
706 }
707 if let Some(mut euid_out) = euid_out {
708 euid_out.write(euid as _);
709 }
710 if let Some(mut suid_out) = suid_out {
711 suid_out.write(suid as _);
712 }
713 Ok(())
714 }
715
716 fn getrlimit(resource: c_int, mut rlim: Out<rlimit>) -> Result<()> {
717 todo_skip!(0, "getrlimit({}, {:p}): not implemented", resource, rlim);
718 rlim.write(rlimit {
719 rlim_cur: RLIM_INFINITY,
720 rlim_max: RLIM_INFINITY,
721 });
722 Ok(())
723 }
724
725 unsafe fn setrlimit(resource: c_int, rlim: *const rlimit) -> Result<()> {
726 todo_skip!(0, "setrlimit({}, {:p}): not implemented", resource, rlim);
727 Err(Errno(EPERM))
728 }
729
730 fn getrusage(who: c_int, r_usage: Out<rusage>) -> Result<()> {
731 todo_skip!(0, "getrusage({}, {:p}): not implemented", who, r_usage);
732 Ok(())
733 }
734
735 fn getsid(pid: pid_t) -> Result<pid_t> {
736 Ok(redox_rt::sys::posix_getsid(pid as usize)? as _)
737 }
738
739 fn gettid() -> pid_t {
740 let thread_fd = Self::current_os_tid().thread_fd;
743 (thread_fd & !syscall::UPPER_FDTBL_TAG)
744 .checked_add(1)
745 .unwrap()
746 .try_into()
747 .unwrap()
748 }
749
750 fn gettimeofday(mut tp: Out<timeval>, tzp: Option<Out<timezone>>) -> Result<()> {
751 let mut redox_tp = redox_timespec::default();
752 syscall::clock_gettime(syscall::CLOCK_REALTIME, &mut redox_tp)?;
753 tp.write(timeval {
754 tv_sec: redox_tp.tv_sec as time_t,
755 tv_usec: (redox_tp.tv_nsec / 1000) as suseconds_t,
756 });
757
758 if let Some(mut tzp) = tzp {
759 tzp.write(timezone {
760 tz_minuteswest: 0,
761 tz_dsttime: 0,
762 });
763 }
764 Ok(())
765 }
766
767 fn getuid() -> uid_t {
768 redox_rt::sys::posix_getresugid().ruid as uid_t
769 }
770
771 fn linkat(fd1: c_int, oldpath: CStr, fd2: c_int, newpath: CStr, flags: c_int) -> Result<()> {
772 if (flags & !(AT_SYMLINK_FOLLOW)) != 0 {
775 return Err(Errno(EINVAL));
776 }
777 let newpath = newpath.to_str().map_err(|_| Errno(EINVAL))?;
778
779 let mut oflags = fcntl::O_PATH | fcntl::O_CLOEXEC | fcntl::O_NOFOLLOW;
784 if (flags & AT_SYMLINK_FOLLOW) == AT_SYMLINK_FOLLOW {
785 oflags &= !fcntl::O_NOFOLLOW;
786 }
787
788 let file = File::openat(fd1, oldpath, oflags)?;
789 let newpath = openat2_path(fd2, newpath, 0)?;
790 syscall::flink(*file as usize, newpath)?;
791 Ok(())
792 }
793
794 fn lseek(fd: c_int, offset: off_t, whence: c_int) -> Result<off_t> {
795 Ok(syscall::lseek(fd as usize, offset as isize, whence as usize)? as off_t)
796 }
797
798 fn mkdirat(dir_fd: c_int, path_name: CStr, mode: mode_t) -> Result<()> {
799 File::createat(
800 dir_fd,
801 path_name,
802 fcntl::O_DIRECTORY | fcntl::O_EXCL | fcntl::O_CLOEXEC,
803 0o777,
804 )?;
805 Ok(())
806 }
807
808 fn mkfifoat(dir_fd: c_int, path_name: CStr, mode: mode_t) -> Result<()> {
809 Sys::mknodat(
810 dir_fd,
811 path_name,
812 syscall::MODE_FIFO as mode_t | (mode & 0o777),
813 0,
814 )
815 }
816
817 fn mknodat(dir_fd: c_int, path_name: CStr, mode: mode_t, dev: dev_t) -> Result<()> {
818 File::createat(dir_fd, path_name, fcntl::O_CREAT | fcntl::O_CLOEXEC, mode)?;
819 Ok(())
820 }
821
822 unsafe fn mlock(addr: *const c_void, len: usize) -> Result<()> {
823 Ok(())
825 }
826
827 unsafe fn mlockall(flags: c_int) -> Result<()> {
828 Ok(())
830 }
831
832 unsafe fn mmap(
833 addr: *mut c_void,
834 len: usize,
835 prot: c_int,
836 flags: c_int,
837 fildes: c_int,
838 off: off_t,
839 ) -> Result<*mut c_void> {
840 if len == 0 {
842 return Err(Errno(EINVAL));
843 }
844 let Some(size) = round_up_to_page_size(len) else {
845 return Err(Errno(ENOMEM));
846 };
847
848 let map = Map {
849 offset: off as usize,
850 size,
851 flags: syscall::MapFlags::from_bits_truncate(
852 ((prot as usize) << 16) | ((flags as usize) & 0xFFFF),
853 ),
854 address: addr as usize,
855 };
856
857 Ok(if flags & MAP_ANONYMOUS == MAP_ANONYMOUS {
858 (unsafe { syscall::fmap(!0, &map) })?
859 } else {
860 (unsafe { syscall::fmap(fildes as usize, &map) })?
861 } as *mut c_void)
862 }
863
864 unsafe fn mremap(
865 addr: *mut c_void,
866 len: usize,
867 new_len: usize,
868 flags: c_int,
869 args: *mut c_void,
870 ) -> Result<*mut c_void> {
871 Err(Errno(ENOSYS))
872 }
873
874 unsafe fn mprotect(addr: *mut c_void, len: usize, prot: c_int) -> Result<()> {
875 let Some(len) = round_up_to_page_size(len) else {
876 return Err(Errno(ENOMEM));
877 };
878 let Some(prot) = syscall::MapFlags::from_bits((prot as usize) << 16) else {
879 return Err(Errno(EINVAL));
880 };
881 (unsafe { syscall::mprotect(addr as usize, len, prot) })?;
882 Ok(())
883 }
884
885 unsafe fn msync(addr: *mut c_void, len: usize, flags: c_int) -> Result<()> {
886 todo_skip!(
887 0,
888 "msync({:p}, 0x{:x}, 0x{:x}): not implemented",
889 addr,
890 len,
891 flags
892 );
893 Err(Errno(ENOSYS))
894 }
902
903 unsafe fn munlock(addr: *const c_void, len: usize) -> Result<()> {
904 Ok(())
906 }
907
908 unsafe fn munlockall() -> Result<()> {
909 Ok(())
911 }
912
913 unsafe fn munmap(addr: *mut c_void, len: usize) -> Result<()> {
914 if len == 0 {
916 return Err(Errno(EINVAL));
917 }
918 let Some(len) = round_up_to_page_size(len) else {
919 return Err(Errno(ENOMEM));
920 };
921 (unsafe { syscall::funmap(addr as usize, len) })?;
922 Ok(())
923 }
924
925 unsafe fn madvise(addr: *mut c_void, len: usize, flags: c_int) -> Result<()> {
926 todo_skip!(
927 0,
928 "madvise({:p}, 0x{:x}, 0x{:x}): not implemented",
929 addr,
930 len,
931 flags
932 );
933 Err(Errno(ENOSYS))
934 }
935
936 unsafe fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> Result<()> {
937 let redox_rqtp = unsafe { (&*rqtp).into() };
938 let mut redox_rmtp = redox_timespec::default();
939 if !rmtp.is_null() {
940 redox_rmtp = unsafe { (&*rmtp).into() };
941 }
942 match redox_rt::sys::posix_nanosleep(&redox_rqtp, &mut redox_rmtp) {
943 Ok(_) => Ok(()),
944 Err(Error { errno: EINTR }) => {
945 unsafe {
946 if !rmtp.is_null() {
947 *rmtp = (&redox_rmtp).into();
948 }
949 };
950 Err(Errno(EINTR))
951 }
952 Err(Error { errno: e }) => Err(Errno(e)),
953 }
954 }
955
956 fn openat(dirfd: c_int, path: CStr, oflag: c_int, mode: mode_t) -> Result<c_int> {
957 let path = path.to_str().map_err(|_| Errno(EINVAL))?;
958
959 let effective_mode = mode & !(redox_rt::sys::get_umask() as mode_t);
967
968 Ok(libredox::openat(dirfd, path, oflag, effective_mode)? as c_int)
969 }
970
971 fn pipe2(mut fds: Out<[c_int; 2]>, flags: c_int) -> Result<()> {
972 fds.write(extra::pipe2(flags as usize)?);
973 Ok(())
974 }
975
976 fn posix_fallocate(fd: c_int, offset: u64, length: NonZeroU64) -> Result<()> {
977 let _guard = FileLock::lock(fd, sys_file::LOCK_EX)?;
980
981 let length = length.get();
988 let total_offset = offset.checked_add(length).ok_or(Errno(EFBIG))?;
989
990 let mut stat: stat = unsafe { mem::zeroed() };
991 unsafe { libredox::fstat(fd as usize, &mut stat)? };
992 let st_size = stat.st_size as u64;
993 if let Some(total_len) = total_offset
997 .checked_sub(st_size)
998 .and_then(|diff| st_size.checked_add(diff))
999 {
1000 let total_len: usize = total_len.try_into().map_err(|_| Errno(EFBIG))?;
1001 libredox::ftruncate(fd as usize, total_len)?;
1002 }
1003
1004 Ok(())
1005 }
1006
1007 fn posix_getdents(fildes: c_int, buf: &mut [u8]) -> Result<usize> {
1008 let current_offset = Self::lseek(fildes, 0, SEEK_CUR)? as u64;
1009 let bytes_read = Self::getdents(fildes, buf, current_offset)?;
1010 if bytes_read == 0 {
1011 return Ok(0);
1012 }
1013 let mut bytes_processed = 0;
1014 let mut next_offset = current_offset;
1015
1016 while bytes_processed < bytes_read {
1017 let remaining_slice = &buf[bytes_processed..];
1018 let (reclen, opaque_next) =
1019 unsafe { Self::dent_reclen_offset(remaining_slice, bytes_processed) }
1020 .ok_or(Errno(EIO))?;
1021 if reclen == 0 {
1022 return Err(Errno(EIO));
1023 }
1024
1025 bytes_processed += reclen as usize;
1026 next_offset = opaque_next;
1027 }
1028
1029 Self::lseek(fildes, next_offset as off_t, SEEK_SET)?;
1030 Ok(bytes_read)
1031 }
1032
1033 unsafe fn rlct_clone(
1034 stack: *mut usize,
1035 os_specific: &mut OsSpecific,
1036 ) -> Result<crate::pthread::OsTid> {
1037 let _guard = CLONE_LOCK.read();
1038 let res = unsafe { redox_rt::thread::rlct_clone_impl(stack, os_specific) };
1039
1040 res.map(|thread_fd| crate::pthread::OsTid { thread_fd })
1041 .map_err(|error| Errno(error.errno))
1042 }
1043
1044 unsafe fn rlct_kill(os_tid: crate::pthread::OsTid, signal: usize) -> Result<()> {
1045 redox_rt::sys::posix_kill_thread(os_tid.thread_fd, signal as u32)?;
1046 Ok(())
1047 }
1048 fn current_os_tid() -> crate::pthread::OsTid {
1049 crate::pthread::OsTid {
1050 thread_fd: RtTcb::current().thread_fd().as_raw_fd(),
1051 }
1052 }
1053
1054 fn read(fd: c_int, buf: &mut [u8]) -> Result<usize> {
1055 let fd = usize::try_from(fd).map_err(|_| Errno(EBADF))?;
1056 Ok(redox_rt::sys::posix_read(fd, buf)?)
1057 }
1058
1059 fn pread(fd: c_int, buf: &mut [u8], offset: off_t) -> Result<usize> {
1060 unsafe {
1061 Ok(syscall::syscall5(
1062 syscall::SYS_READ2,
1063 fd as usize,
1064 buf.as_mut_ptr() as usize,
1065 buf.len(),
1066 offset as usize,
1067 !0,
1068 )?)
1069 }
1070 }
1071
1072 fn fpath(fildes: c_int, out: &mut [u8]) -> Result<usize> {
1073 let mut buf = [0; limits::PATH_MAX];
1076 let count = syscall::fpath(fildes as usize, &mut buf)?;
1077
1078 let redox_path = str::from_utf8(&buf[..count])
1079 .ok()
1080 .and_then(|x| redox_path::RedoxPath::from_absolute(x))
1081 .ok_or(Errno(EINVAL))?;
1082
1083 let (scheme, reference) = redox_path.as_parts().ok_or(Errno(EINVAL))?;
1084
1085 let mut cursor = io::Cursor::new(out);
1086 let res = match scheme.as_ref() {
1087 "file" => write!(cursor, "/{}", reference.as_ref().trim_start_matches('/')),
1088 _ => write!(
1089 cursor,
1090 "/scheme/{}/{}",
1091 scheme.as_ref(),
1092 reference.as_ref().trim_start_matches('/')
1093 ),
1094 };
1095 match res {
1096 Ok(()) => Ok(cursor.position() as usize),
1097 Err(_err) => Err(Errno(ENAMETOOLONG)),
1098 }
1099 }
1100
1101 fn readlinkat(dirfd: c_int, path: CStr, out: &mut [u8]) -> Result<usize> {
1102 let path = str::from_utf8(path.to_bytes()).map_err(|_| Errno(ENOENT))?;
1103 let file = openat2(
1104 dirfd,
1105 path,
1106 0,
1107 fcntl::O_RDONLY | fcntl::O_SYMLINK | fcntl::O_CLOEXEC,
1108 )?;
1109 Sys::read(*file, out)
1110 }
1111
1112 fn renameat2(
1113 old_dir: c_int,
1114 old_path: CStr,
1115 new_dir: c_int,
1116 new_path: CStr,
1117 flags: c_uint,
1118 ) -> Result<()> {
1119 const MASK: c_uint = !RENAME_NOREPLACE;
1120 if MASK & flags != 0 {
1121 return Err(Errno(EOPNOTSUPP));
1122 }
1123
1124 let new_path = new_path.to_str().map_err(|_| Errno(EINVAL))?;
1125 if flags & RENAME_NOREPLACE != 0
1127 && let Ok(fd) =
1128 libredox::openat(new_dir, &new_path, fcntl::O_PATH | fcntl::O_CLOEXEC, 0)
1129 .map(FdGuard::new)
1130 {
1131 return Err(Errno(EEXIST));
1132 }
1133
1134 let old_path = old_path.to_str().map_err(|_| Errno(EINVAL))?;
1135 let source = openat2(old_dir, old_path, 0, fcntl::O_NOFOLLOW | fcntl::O_PATH)?;
1137
1138 let target = openat2_path(new_dir, new_path, 0)?;
1139 syscall::frename(*source as usize, target)
1141 .map(|_| ())
1142 .map_err(Into::into)
1143 }
1144
1145 fn sched_yield() -> Result<()> {
1146 syscall::sched_yield()?;
1147 Ok(())
1148 }
1149
1150 unsafe fn setgroups(size: size_t, list: *const gid_t) -> Result<()> {
1151 todo_skip!(0, "setgroups({}, {:p}): not implemented", size, list);
1153 Err(Errno(ENOSYS))
1154 }
1155
1156 fn setpgid(pid: pid_t, pgid: pid_t) -> Result<()> {
1157 redox_rt::sys::posix_setpgid(pid as usize, pgid as usize)?;
1158 Ok(())
1159 }
1160
1161 fn setpriority(which: c_int, who: id_t, prio: c_int) -> Result<()> {
1162 let clamped_prio = prio.clamp(-20, 19);
1163 let kernel_prio = (20 + clamped_prio) as u32;
1164
1165 match redox_rt::sys::posix_setpriority(which, who as u32, kernel_prio) {
1166 Ok(_) => Ok(()),
1167 Err(e) => Err(Errno(e.errno)),
1168 }
1169 }
1170
1171 fn setsid() -> Result<c_int> {
1172 Ok(redox_rt::sys::posix_setsid()? as c_int)
1173 }
1174
1175 fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> Result<()> {
1176 redox_rt::sys::posix_setresugid(
1177 &Resugid {
1178 ruid: None,
1179 euid: None,
1180 suid: None,
1181 rgid: cvt_uid(rgid)?,
1182 egid: cvt_uid(egid)?,
1183 sgid: cvt_uid(sgid)?,
1184 },
1185 None,
1186 )?;
1187 Ok(())
1188 }
1189
1190 fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> Result<()> {
1191 redox_rt::sys::posix_setresugid(
1192 &Resugid {
1193 ruid: cvt_uid(ruid)?,
1194 euid: cvt_uid(euid)?,
1195 suid: cvt_uid(suid)?,
1196 rgid: None,
1197 egid: None,
1198 sgid: None,
1199 },
1200 None,
1201 )?;
1202 Ok(())
1203 }
1204
1205 unsafe fn spawn(
1206 program: CStr,
1207 fac: Option<&crate::header::spawn::posix_spawn_file_actions_t>,
1208 fat: Option<&crate::header::spawn::posix_spawnattr_t>,
1209 argv: NulTerminated<*mut c_char>,
1210 envp: Option<NulTerminated<*mut c_char>>,
1211 ) -> Result<pid_t> {
1212 use crate::header::spawn::Flags;
1213 let child = redox_rt::proc::new_child_process(&redox_rt::proc::ForkArgs::Managed)?;
1214 let mut cwd = path::clone_cwd().unwrap_or_default();
1215 let mut cwd_fd = FdGuard::open(cwd.as_str(), syscall::O_STAT)?.to_upper()?;
1216 let proc_fd = child.proc_fd.unwrap();
1217 let curr_proc_fd = redox_rt::current_proc_fd();
1218 let cur_filetable_fd = RtTcb::current().thread_fd().dup_into_upper(b"filetable")?;
1219 let file_table = cur_filetable_fd.dup_into_upper(b"copy")?;
1220
1221 {
1222 let new_file_table = child.thr_fd.dup_into_upper(b"current-filetable")?;
1223 new_file_table.write(&file_table.as_raw_fd().to_ne_bytes())?;
1224 }
1225
1226 let mut args = Vec::new();
1227 let mut envs = Vec::new();
1228
1229 for arg in argv {
1230 args.push(unsafe { CStr::from_ptr(*arg).to_chars() });
1231 }
1232
1233 if let Some(envp) = envp {
1234 for env in envp {
1235 envs.push(unsafe { CStr::from_ptr(*env).to_chars() });
1236 }
1237 }
1238
1239 args[0] = &program.to_bytes();
1240
1241 let new_file_table = child.thr_fd.dup_into_upper(b"filetable-binary")?;
1242
1243 if let Some(fac) = fac {
1244 for action in fac {
1245 match action {
1246 crate::header::spawn::Action::Open {
1247 fd,
1248 path,
1249 flag,
1250 mode,
1251 } => {
1252 let dirfd = {
1253 FdGuard::open(cwd.as_str(), syscall::O_STAT)?
1256 };
1257 let src_fd = Sys::openat(
1258 dirfd.as_c_fd().ok_or(Errno(EMFILE))?,
1259 CStr::borrow(&path),
1260 flag,
1261 mode,
1262 )? as usize;
1263 new_file_table.call_wo(
1264 &src_fd.to_ne_bytes(),
1265 syscall::CallFlags::FD,
1266 &[u64::try_from(fd).map_err(|_| Errno(EBADFD))?],
1267 )?;
1268 }
1269 crate::header::spawn::Action::Close(fd) => {
1270 new_file_table.call_wo(
1271 &(fd as usize).to_ne_bytes(),
1272 syscall::CallFlags::empty(),
1273 &[syscall::flag::FileTableVerb::Close as u64],
1274 )?;
1275 }
1276 crate::header::spawn::Action::Chdir(path) => {
1277 let cwd_str =
1278 core::str::from_utf8(path.as_bytes()).map_err(|_| Errno(EINVAL))?;
1279 cwd = to_cwd_path(cwd_str)?;
1280 let fd = FdGuard::open(cwd.as_str(), syscall::O_STAT)?.to_upper()?;
1281 cwd_fd = fd;
1282 }
1283 crate::header::spawn::Action::FChdir(fd) => {
1284 let mut buf = CwdPath::zero_filled();
1285 unsafe {
1286 let res = Sys::fpath(fd, buf.as_bytes_mut())?;
1288 buf.set_len(res);
1289 }
1290 cwd = buf;
1291 let fd = FdGuard::open(cwd.as_str(), syscall::O_STAT)?.to_upper()?;
1292 cwd_fd = fd;
1293 }
1294 crate::header::spawn::Action::Dup2(old, new) => {
1295 new_file_table.call_wo(
1296 [(old as usize).to_ne_bytes(), (new as usize).to_ne_bytes()]
1297 .into_iter()
1298 .flatten()
1299 .collect::<Vec<u8>>()
1300 .as_slice(),
1301 syscall::CallFlags::empty(),
1302 &[syscall::flag::FileTableVerb::Dup2 as u64],
1303 )?;
1304 }
1305 }
1306 }
1307 }
1308
1309 let dirfd = {
1310 FdGuard::open(cwd.as_str(), syscall::O_STAT)?
1313 };
1314
1315 let executable = Sys::openat(
1316 dirfd.as_c_fd().ok_or(Errno(EMFILE))?,
1317 program,
1318 fcntl::O_RDONLY,
1319 0,
1320 )?;
1321 let executable = FdGuard::new(executable as usize).to_upper()?;
1322 let mut executable_stat = syscall::Stat::default();
1323 executable.fstat(&mut executable_stat)?;
1324 drop(dirfd);
1325
1326 new_file_table.call_wo(
1336 &new_file_table.as_raw_fd().to_ne_bytes(),
1337 syscall::CallFlags::FD | syscall::CallFlags::FD_CLONE,
1338 &[new_file_table.as_raw_fd() as u64],
1339 )?;
1340
1341 {
1342 let fds_to_close = {
1343 let guard = redox_rt::current_filetable();
1344 let mut fds = alloc::vec::Vec::new();
1345 for (fd, flags) in guard.iter() {
1346 if flags & redox_protocols::protocol::O_CLOEXEC
1347 == redox_protocols::protocol::O_CLOEXEC
1348 || fd == executable.as_raw_fd()
1349 {
1350 fds.push(fd);
1351 }
1352 }
1353
1354 fds.push(cur_filetable_fd.as_raw_fd());
1355
1356 fds
1357 };
1358
1359 let fds_to_close_bytes: &[u8] = unsafe {
1360 core::slice::from_raw_parts(
1361 fds_to_close.as_ptr() as *mut u8,
1362 fds_to_close.len() * core::mem::size_of::<usize>(),
1363 )
1364 };
1365
1366 let _ = new_file_table.call_wo(
1367 fds_to_close_bytes,
1368 syscall::CallFlags::empty(),
1369 &[syscall::FileTableVerb::Close as u64],
1370 );
1371 }
1372
1373 new_file_table.call_wo(
1374 &cwd_fd.as_raw_fd().to_ne_bytes(),
1375 syscall::CallFlags::FD | syscall::CallFlags::FD_CLONE,
1376 &[cwd_fd.as_raw_fd() as u64],
1377 )?;
1378
1379 let extra_info = redox_rt::proc::ExtraInfo {
1380 cwd: Some(cwd.as_bytes()),
1381 sigignmask: redox_rt::signal::get_sigignmask_to_inherit(),
1384 sigprocmask: if let Some(fat) = fat
1385 && Flags::from_bits(fat.flags)
1386 .unwrap()
1387 .contains(Flags::POSIX_SPAWN_SETSIGMASK)
1388 {
1389 fat.sigmask
1390 } else {
1391 redox_rt::signal::get_sigmask().unwrap()
1392 },
1393 umask: redox_rt::sys::get_umask(),
1394 thr_fd: child.thr_fd.as_raw_fd(),
1395 proc_fd: proc_fd.as_raw_fd(),
1396 ns_fd: redox_rt::current_namespace_fd().ok(),
1397 cwd_fd: Some(cwd_fd.as_raw_fd()),
1398 filetable_fd: Some(new_file_table.as_raw_fd()),
1399 same_process: false,
1400 };
1401
1402 if let Some(attr) = fat {
1403 let flags = Flags::from_bits(attr.flags).ok_or(Errno(EINVAL))?;
1404
1405 if flags.contains(Flags::POSIX_SPAWN_SETPGROUP) && attr.pgroup != 0 {
1406 redox_rt::sys::posix_setpgid(
1407 proc_fd.as_raw_fd(),
1408 usize::try_from(attr.pgroup).map_err(|_| Errno(EINVAL))?,
1409 )?;
1410 }
1411
1412 let set_schedparam = || -> Result<()> {
1413 if setpriority(
1414 PRIO_PROCESS,
1415 proc_fd.as_raw_fd() as id_t,
1416 attr.param.sched_priority,
1417 ) as usize
1418 != 0
1419 {
1420 Err(Errno(ERRNO.get()))
1421 } else {
1422 Ok(())
1423 }
1424 };
1425 let set_scheduler = || -> Result<()> { todo!() };
1426
1427 if flags.contains(Flags::POSIX_SPAWN_SETSCHEDULER) {
1429 set_schedparam()?;
1430 set_scheduler()?;
1431 } else if flags.contains(Flags::POSIX_SPAWN_SETSCHEDPARAM) {
1432 set_schedparam()?;
1433 }
1434
1435 let parent_resugid = redox_rt::sys::posix_getresugid();
1436
1437 redox_rt::sys::posix_setresugid(
1438 &Resugid {
1439 ruid: None,
1440 euid: Some(if executable_stat.st_mode as mode_t & S_ISUID == S_ISUID {
1441 executable_stat.st_uid
1442 } else if flags.contains(Flags::POSIX_SPAWN_RESETIDS) {
1443 parent_resugid.ruid
1444 } else {
1445 parent_resugid.euid
1446 }),
1447 suid: None,
1448 rgid: None,
1449 egid: Some(if executable_stat.st_mode as mode_t & S_ISGID == S_ISGID {
1450 executable_stat.st_gid
1451 } else if flags.contains(Flags::POSIX_SPAWN_RESETIDS) {
1452 parent_resugid.rgid
1453 } else {
1454 parent_resugid.egid
1455 }),
1456 sgid: None,
1457 },
1458 Some(proc_fd.as_raw_fd()),
1459 )?;
1460
1461 }
1464
1465 let program = program.to_bytes();
1466
1467 if let Some(redox_rt::proc::FexecResult::Interp {
1468 path: interp_path,
1469 interp_override,
1470 }) = redox_rt::proc::fexec_impl(
1471 executable,
1472 &child.thr_fd,
1473 &proc_fd,
1474 program,
1475 args.as_slice(),
1476 envs.as_slice(),
1477 &extra_info,
1478 None,
1479 )? {
1480 let interp_path =
1481 CStr::from_bytes_with_nul(&interp_path).map_err(|_| Errno(ENOEXEC))?;
1482
1483 let interpreter = File::open(interp_path, fcntl::O_RDONLY | fcntl::O_CLOEXEC)
1484 .map_err(|_| Errno(ENOENT))?;
1485
1486 redox_rt::proc::fexec_impl(
1487 FdGuard::new(interpreter.fd as usize).to_upper().unwrap(),
1488 &child.thr_fd,
1489 &proc_fd,
1490 program,
1491 args.as_slice(),
1492 envs.as_slice(),
1493 &extra_info,
1494 Some(interp_override),
1495 )
1496 .unwrap();
1497 }
1498
1499 let start_fd = child.thr_fd.dup_into_upper(b"start")?;
1500 start_fd.write(&[0])?;
1501
1502 Ok(pid_t::try_from(child.pid).unwrap())
1503 }
1504
1505 fn symlinkat(path1: CStr, fd: c_int, path2: CStr) -> Result<()> {
1506 let mut file = File::createat(
1507 fd,
1508 path2,
1509 fcntl::O_WRONLY | fcntl::O_SYMLINK | fcntl::O_CLOEXEC,
1510 0o777,
1511 )?;
1512
1513 file.write(path1.to_bytes())
1514 .map_err(|err| Errno(err.raw_os_error().unwrap_or(EIO)))?;
1515
1516 Ok(())
1517 }
1518
1519 fn sync() -> Result<()> {
1520 Ok(())
1521 }
1522
1523 fn timer_create(clock_id: clockid_t, evp: &sigevent, mut timerid: Out<timer_t>) -> Result<()> {
1524 if evp.sigev_notify == SIGEV_THREAD {
1525 if evp.sigev_notify_function.is_none() {
1526 return Err(Errno(EINVAL));
1527 }
1528 } else if evp.sigev_notify == SIGEV_SIGNAL {
1529 const N_SIG: i32 = NSIG as i32;
1530 const RT_MIN: i32 = SIGRTMIN as i32;
1531 const RT_MAX: i32 = SIGRTMIN as i32;
1532 match evp.sigev_signo {
1533 0..N_SIG => {}
1534 RT_MIN..=RT_MAX => {}
1535 _ => {
1536 return Err(Errno(EINVAL));
1537 }
1538 }
1539 }
1540
1541 let path = match clock_id {
1542 CLOCK_REALTIME => "/scheme/time/1",
1543 CLOCK_MONOTONIC => "/scheme/time/4",
1544 _ => return Err(Errno(EINVAL)),
1545 };
1546 let timerfd = FdGuard::open_into_upper(&path, syscall::O_RDWR)?;
1547 let eventfd = FdGuard::new(Error::demux(unsafe {
1548 event::redox_event_queue_create_v1(0)
1549 })?)
1550 .to_upper()?;
1551
1552 let timer_st = timer_internal_t {
1553 clockid: clock_id,
1554 timerfd: timerfd.take(),
1555 eventfd: eventfd.take(),
1556 evp: (*evp).clone(),
1557 thread: ptr::null_mut(),
1558 next_wake_time: itimerspec::default(),
1559 next_wake_version: 0,
1560 process_pid: Sys::getpid(),
1561 };
1562 let timers = &mut TIMERS.lock().0;
1563 let mut memory_pointer: *mut timer_internal_t = ptr::null_mut();
1565 unsafe {
1566 let result = posix_memalign(
1567 (&mut memory_pointer as *mut *mut timer_internal_t).cast(),
1568 align_of::<timer_internal_t>(),
1569 size_of::<timer_internal_t>(),
1570 );
1571 assert_eq!(result, 0, "Failed to allocate or invalid alignment");
1572 };
1573
1574 let pointer = {
1575 ptr::NonNull::new(memory_pointer)
1576 .expect("Pointer is guaranteed to not be null if posix_memalign returns 0")
1577 };
1578
1579 unsafe {
1581 pointer.as_ptr().write(timer_st);
1584 }
1585 let timer_ptr = pointer.as_ptr() as timer_t;
1586 timers.insert(timer_ptr);
1587
1588 timerid.write(timer_ptr);
1589
1590 Ok(())
1591 }
1592
1593 fn timer_delete(timerid: timer_t) -> Result<()> {
1594 let timers = &mut TIMERS.lock().0;
1595 let removed = timers.remove(&timerid);
1596 if !removed {
1597 return Err(Errno(EINVAL));
1598 }
1599 let timer_st = unsafe { timer_internal_t::from_raw(timerid) };
1600 let _ = redox_rt::sys::close(timer_st.timerfd);
1601 let _ = redox_rt::sys::close(timer_st.eventfd);
1602 if !timer_st.thread.is_null() {
1603 let _ = unsafe { pthread_cancel(timer_st.thread) };
1604 }
1605 unsafe { free(timerid) };
1606
1607 Ok(())
1608 }
1609
1610 fn timer_gettime(timerid: timer_t, mut value: Out<itimerspec>) -> Result<()> {
1611 let timers = &mut TIMERS.lock().0;
1612 if !timers.contains(&timerid) {
1613 return Err(Errno(EINVAL));
1614 }
1615 let timer_st = unsafe { timer_internal_t::from_raw(timerid) };
1616 let mut now = timespec::default();
1617 Self::clock_gettime(timer_st.clockid, Out::from_mut(&mut now))?;
1618 if timer_st.evp.sigev_notify == SIGEV_NONE {
1619 if timespec::subtract(&timer_st.next_wake_time.it_value, &now).is_none() {
1620 let _ = timer_update_wake_time(timer_st);
1622 }
1623 }
1624 let remaining = &timer_st.next_wake_time.it_value;
1625 value.write(if remaining.is_zero() {
1626 itimerspec::default()
1628 } else {
1629 itimerspec {
1630 it_interval: timer_st.next_wake_time.it_interval.clone(),
1631 it_value: timespec::subtract(remaining, &now).unwrap_or_default(),
1632 }
1633 });
1634
1635 Ok(())
1636 }
1637
1638 fn timer_settime(
1639 timerid: timer_t,
1640 flags: c_int,
1641 value: &itimerspec,
1642 ovalue: Option<Out<itimerspec>>,
1643 ) -> Result<()> {
1644 if let Some(ovalue) = ovalue {
1645 Self::timer_gettime(timerid, ovalue)?;
1646 }
1647
1648 let timers = &mut TIMERS.lock().0;
1649 if !timers.contains(&timerid) {
1650 return Err(Errno(EINVAL));
1651 }
1652 let timer_st = unsafe { timer_internal_t::from_raw(timerid) };
1653
1654 if value.it_value.is_zero() {
1655 timer_st.next_wake_version += 1;
1656 return Ok(());
1657 }
1658
1659 timer_st.next_wake_time = {
1660 let mut val = value.clone();
1661 if flags & TIMER_ABSTIME == 0 {
1662 let mut now = timespec::default();
1663 Self::clock_gettime(timer_st.clockid, Out::from_mut(&mut now))?;
1664 val.it_value = timespec::add(&now, &val.it_value).ok_or(Errno(EINVAL))?;
1665 }
1666 val
1667 };
1668
1669 Error::demux(unsafe {
1670 event::redox_event_queue_ctl_v1(timer_st.eventfd, timer_st.timerfd, 1, 0)
1671 })?;
1672
1673 let buf_to_write = syscall::TimeSpec::from(&timer_st.next_wake_time.it_value);
1674
1675 let bytes_written = redox_rt::sys::posix_write(timer_st.timerfd, &buf_to_write)?;
1676
1677 if bytes_written < mem::size_of::<timespec>() {
1678 return Err(Errno(EIO));
1679 }
1680
1681 if timer_st.thread.is_null() {
1682 timer_st.thread = match timer_st.evp.sigev_notify {
1683 SIGEV_THREAD | SIGEV_SIGNAL => {
1684 let mut tid = pthread_t::default();
1685 let result = unsafe {
1686 pthread_create(
1687 &mut tid as *mut _,
1688 ptr::null(),
1689 timer_routine,
1690 timerid as *mut c_void,
1691 )
1692 };
1693 if result != 0 {
1694 return Err(Errno(result));
1695 }
1696 tid
1697 }
1698 SIGEV_NONE => ptr::null_mut(),
1699 _ => {
1700 return Err(Errno(EINVAL));
1701 }
1702 };
1703 }
1704
1705 Ok(())
1706 }
1707
1708 fn umask(mask: mode_t) -> mode_t {
1709 let new_effective_mask = mask & mode_t::from(MODE_PERM) & !S_ISVTX;
1710 (redox_rt::sys::swap_umask(new_effective_mask as u32) as mode_t) & !S_ISVTX
1711 }
1712
1713 fn uname(mut utsname: Out<utsname>) -> Result<(), Errno> {
1714 fn gethostname(mut name: Out<[u8]>) -> io::Result<()> {
1715 if name.is_empty() {
1716 return Ok(());
1717 }
1718
1719 let mut file = File::open(c"/etc/hostname".into(), fcntl::O_RDONLY | fcntl::O_CLOEXEC)?;
1720
1721 let mut read = 0;
1722 let name_len = name.len();
1723 loop {
1724 match file.read_out(name.subslice(read, name_len - 1))? {
1725 0 => break,
1726 n => read += n,
1727 }
1728 }
1729 name.index(read).write(0);
1730 Ok(())
1731 }
1732 out_project! {
1733 let utsname {
1734 nodename: [c_char; UTSLENGTH],
1735 sysname: [c_char; UTSLENGTH],
1736 release: [c_char; UTSLENGTH],
1737 machine: [c_char; UTSLENGTH],
1738 version: [c_char; UTSLENGTH],
1739 domainname: [c_char; UTSLENGTH],
1740 } = utsname;
1741 }
1742
1743 match gethostname(nodename.as_slice_mut().cast_slice_to::<u8>()) {
1744 Ok(_) => (),
1745 Err(_) => return Err(Errno(EIO)),
1746 }
1747
1748 let file_path = c"/scheme/sys/uname".into();
1749 let mut file = match File::open(file_path, fcntl::O_RDONLY | fcntl::O_CLOEXEC) {
1750 Ok(ok) => ok,
1751 Err(_) => return Err(Errno(EIO)),
1752 };
1753 let mut lines = BufReader::new(&mut file).lines();
1754
1755 let mut read_line = |mut dst: Out<[u8]>| {
1756 let line = match lines.next() {
1758 Some(Ok(l)) => CString::new(l).map_err(|_| Errno(EIO))?,
1759 None | Some(Err(_)) => return Err(Errno(EIO)),
1760 };
1761
1762 let line_slice: &[u8] = line.as_bytes_with_nul();
1763 if line_slice.len() > UTSLENGTH {
1764 return Err(Errno(EIO));
1765 }
1766
1767 dst.copy_common_length_from_slice(line_slice);
1768 Ok(())
1769 };
1770
1771 read_line(sysname.as_slice_mut().cast_slice_to::<u8>())?;
1783 read_line(release.as_slice_mut().cast_slice_to::<u8>())?;
1784 read_line(machine.as_slice_mut().cast_slice_to::<u8>())?;
1785 read_line(version.as_slice_mut().cast_slice_to::<u8>())?;
1786
1787 domainname.as_slice_mut().zero();
1790
1791 Ok(())
1792 }
1793
1794 fn unlinkat(fd: c_int, path: CStr, flags: c_int) -> Result<()> {
1795 if (flags & !AT_REMOVEDIR) != 0 {
1796 return Err(Errno(EINVAL));
1797 }
1798 let path = path.to_str().map_err(|_| Errno(EINVAL))?;
1799 let path = openat2_path(fd, path, 0)?;
1800 let canon = canonicalize(&path)?;
1801 redox_rt::sys::unlink(&canon, flags.try_into().map_err(|_| Errno(EINVAL))?)?;
1802 Ok(())
1803 }
1804
1805 fn waitpid(pid: pid_t, stat_loc: Option<Out<'_, c_int>>, options: c_int) -> Result<pid_t> {
1806 let res = None;
1807 let mut status = 0;
1808
1809 let options = usize::try_from(options)
1810 .ok()
1811 .and_then(WaitFlags::from_bits)
1812 .ok_or(Errno(EINVAL))?;
1813
1814 let inner = |status: &mut usize, flags| {
1815 redox_rt::sys::sys_waitpid(WaitpidTarget::from_posix_arg(pid as isize), status, flags)
1816 };
1817
1818 let state = ptrace::init_state();
1821 let res = res.unwrap_or_else(|| {
1847 loop {
1848 let res = inner(&mut status, options | WaitFlags::WUNTRACED);
1849
1850 if !wifstopped(status)
1852 || options.contains(WaitFlags::WUNTRACED)
1853 || ptrace::is_traceme(pid)
1854 {
1855 break res;
1856 }
1857 }
1858 });
1859
1860 if let Some(mut stat_loc) = stat_loc {
1862 stat_loc.write(status as c_int);
1863 }
1864
1865 Ok(res? as pid_t)
1866 }
1867
1868 fn write(fd: c_int, buf: &[u8]) -> Result<usize> {
1869 let fd = usize::try_from(fd).map_err(|_| Errno(EBADFD))?;
1870 Ok(redox_rt::sys::posix_write(fd, buf)?)
1871 }
1872 fn pwrite(fd: c_int, buf: &[u8], offset: off_t) -> Result<usize> {
1873 unsafe {
1874 Ok(syscall::syscall5(
1875 syscall::SYS_WRITE2,
1876 fd as usize,
1877 buf.as_ptr() as usize,
1878 buf.len(),
1879 offset as usize,
1880 !0,
1881 )?)
1882 }
1883 }
1884
1885 fn verify() -> bool {
1886 (unsafe { syscall::syscall5(syscall::number::SYS_YIELD, !0, !0, !0, !0, !0) }).is_ok()
1888 }
1889
1890 unsafe fn exit_thread(stack_base: *mut (), stack_size: usize) -> ! {
1891 unsafe { redox_rt::thread::exit_this_thread(stack_base, stack_size) }
1892 }
1893}
1894
1895impl Sys {
1896 fn relative_to_absolute_foffset(
1897 fd: usize,
1898 whence: c_short,
1899 start: off_t,
1900 len: off_t,
1901 ) -> Result<(off_t, off_t)> {
1902 match whence as i32 {
1904 SEEK_SET => {
1905 let (start, len) = if len < 0 {
1906 (start + len, -len)
1907 } else {
1908 (start, len)
1909 };
1910
1911 if start < 0 {
1912 return Err(Errno(EINVAL));
1913 }
1914
1915 assert!(len >= 0);
1916 Ok((start, len))
1917 }
1918 c => {
1920 log::warn!(
1921 "Sys::relative_to_absolute_foffset: whence={whence} not yet implemented"
1922 );
1923 Ok((0, 0))
1924 }
1925 }
1926 }
1927}