Skip to main content

relibc/platform/redox/
mod.rs

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
102/// Redox syscall implementation of [`Pal`].
103pub 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        // On first invocation, allocate a buffer for brk
153        if unsafe { BRK_CUR }.is_null() {
154            // 4 megabytes of RAM ought to be enough for anybody
155            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            // Lookup what previous brk() invocations have set the address to
176            Ok(unsafe { BRK_CUR })
177        } else if unsafe { BRK_CUR } <= addr && addr < unsafe { BRK_END } {
178            // It's inside buffer, return
179            unsafe { BRK_CUR = addr };
180            Ok(addr)
181        } else {
182            // It was outside of valid range
183            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                // If the path is empty but `AT_EMPTY_PATH` is **not** set, bail out.
302                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                // If the path is empty but `AT_EMPTY_PATH` is **not** set, bail out.
326                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                // `pid` if traditional POSIX otherwise 0
404                let val =
405                    match syscall::std_fs_call(fd as usize, bytes_of_slice_mut(payload), &meta) {
406                        // According to POSIX Issue 8:
407                        // > If no lock is found that would prevent this lock from being created, then
408                        // > the structure shall be left unchanged except for the lock type in `l_type`
409                        // > which shall be set to F_UNLCK.
410                        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        // TODO: "Needs" syscall update
450        syscall::fsync(fd as usize)?;
451        Ok(())
452    }
453
454    fn flock(_fd: c_int, _operation: c_int) -> Result<()> {
455        // TODO: Redox does not have file locking yet
456        Ok(())
457    }
458
459    unsafe fn fork() -> Result<pid_t> {
460        // TODO: Find way to avoid lock.
461        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        // `path` should be non-null.
468        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                // If the path is empty but `AT_EMPTY_PATH` is **not** set, bail out.
480                return Err(Errno(ENOENT));
481            }
482        }
483
484        let file = openat2(dirfd, path, flags, fcntl::O_PATH)?;
485        // Close the file descriptor after fstat(2) regardless of success or failure.
486        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<&timespec>) -> 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                // If the path is empty but `AT_EMPTY_PATH` is **not** set, bail out.
539                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        // Redox getdents takes an explicit (opaque) offset, so this is a no-op.
558        Ok(())
559    }
560    // NOTE: fn is unsafe, but this just means we can assume more things. impl is safe
561    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 scheme does not send a NUL byte, this shouldn't be able to cause UB for the caller.
566        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        // FIXME: this operation doesn't scale when group/passwd file grows
587
588        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            //TODO: /dev/random equivalent
662            "/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        //TODO: store fd internally
673        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        // This is used by pthread mutexes for reentrant checks and must be nonzero
741        // and unique for each thread in the same process (but not cross-process)
742        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        // make sure the flags passed are valid.
773        // valid states: AT_SYMLINK_FOLLOW, or 0.
774        if (flags & !(AT_SYMLINK_FOLLOW)) != 0 {
775            return Err(Errno(EINVAL));
776        }
777        let newpath = newpath.to_str().map_err(|_| Errno(EINVAL))?;
778
779        // By default, we don't follow the symlink if there is one.
780        // We only follow it if AT_SYMLINK_FOLLOW is passed in flags.
781        // We represent this by setting O_NOFOLLOW by default, and clearing it
782        // if AT_SYMLINK_FOLLOW is present.
783        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        // Redox never swaps
824        Ok(())
825    }
826
827    unsafe fn mlockall(flags: c_int) -> Result<()> {
828        // Redox never swaps
829        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        // 0 is invalid per spec
841        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        /* TODO
895        syscall::msync(
896            addr as usize,
897            round_up_to_page_size(len),
898            flags
899        )?;
900        */
901    }
902
903    unsafe fn munlock(addr: *const c_void, len: usize) -> Result<()> {
904        // Redox never swaps
905        Ok(())
906    }
907
908    unsafe fn munlockall() -> Result<()> {
909        // Redox never swaps
910        Ok(())
911    }
912
913    unsafe fn munmap(addr: *mut c_void, len: usize) -> Result<()> {
914        // 0 is invalid per spec
915        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        // POSIX states that umask should affect the following:
960        //
961        // open, openat, creat, mkdir, mkdirat,
962        // mkfifo, mkfifoat, mknod, mknodat,
963        // mq_open, and sem_open,
964        //
965        // all of which (the ones that exist thus far) currently call this function.
966        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        // Redox doesn't actually have flock yet but presumably the file will need to be locked to
978        // avoid accidentally truncating it if the length changes.
979        let _guard = FileLock::lock(fd, sys_file::LOCK_EX)?;
980
981        // posix_fallocate is less nuanced than the Linux syscall fallocate.
982        // If the byte range is already allocated, posix_fallocate doesn't do any extra work.
983        // If the byte range is unallocated; free, uninitialized bytes are reserved.
984        // posix_fallocate does not shrink files.
985        //
986        // The main purpose of it is to ensure subsequent writes to a byte range don't fail.
987        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        // The difference between total_offset and the file size is the number of bytes to
994        // allocate. So, if it's negative then the file is already large enough and we don't
995        // need to do any extra work.
996        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        // Since this is used by realpath, it converts from the old format to the new one for
1074        // compatibility reasons
1075        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        // Fail if the target exists with RENAME_NOREPLACE.
1126        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        // oflags are the same as Sys::rename above.
1136        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        // I'm avoiding Sys::rename to avoid reallocating a CString from a String.
1140        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
1152        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                            // TODO: Cannot use cwd_fd (being an upper fd), also
1254                            // cannot set cwd_fd as regular fd then call to_upper later
1255                            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                            // SAFETY: Sys::fpath is using str::from_utf8 already
1287                            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            // TODO: Cannot use cwd_fd (being an upper fd), also
1311            // cannot set cwd_fd as regular fd then call to_upper later
1312            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        // println!(
1327        //     "RELIBC: spawn {:?} at {:?}",
1328        //     args.iter()
1329        //         .map(|s| str::from_utf8(s).unwrap_or("???"))
1330        //         .collect::<Vec<_>>()
1331        //         .as_slice(),
1332        //     cwd.as_str()
1333        // );
1334
1335        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            // Signals set to be ignored by the calling process
1382            // must also be ignored by the child process
1383            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            // scheduling paramters must be set regardless of whether the flag POSIX_SPAWN_SETSCHEDPARAM is set
1428            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            // if POSIX_SPAWN_SETSIGDEF flag is set, the signals specified in
1462            // sigdefault must have default actions
1463        }
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        // allocate enough memory on the heap to store one timer_internal_t
1564        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        // move value from the stack to the location we allocated on the heap
1580        unsafe {
1581            // Safety: If non-null, posix_memalign gives us a pointer that is valid
1582            // for writes and properly aligned.
1583            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                // error here means the timer is disarmed
1621                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            // disarmed
1627            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            // TODO: set nul byte without allocating CString
1757            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        // The file format is currently as follows:
1772        // <sysname>\n e.g. "Redox"
1773        // <release>\n e.g. "0.9.0"
1774        // <machine>\n e.g. "x86_64"
1775        // <version>\n e.g. "<commit hash of kernel>
1776
1777        // A future file format might add the domainname.
1778
1779        // nodename is handled above with /etc/hostname, and the domainname is
1780        // currently zeroed out.
1781
1782        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        // Redox doesn't provide domainname in uname scheme
1788        //read_line(domainname.as_slice_mut().cast_slice_to::<u8>())?;
1789        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        // First, allow ptrace to handle waitpid
1819        // TODO: Handle special PIDs here (such as -1)
1820        let state = ptrace::init_state();
1821        // TODO: Fix ptrace deadlock seen during openposixtestsuite signals tests
1822        // let mut sessions = state.sessions.lock();
1823        // if let Ok(session) = ptrace::get_session(&mut sessions, pid) {
1824        //     if !options.contains(WaitFlags::WNOHANG) {
1825        //         let mut _event = PtraceEvent::default();
1826        //         let _ = (&mut &session.tracer).read(&mut _event);
1827
1828        //         res = Some(inner(
1829        //             &mut status,
1830        //             options | WaitFlags::WNOHANG | WaitFlags::WUNTRACED,
1831        //         ));
1832        //         if res == Some(Ok(0)) {
1833        //             // WNOHANG, just pretend ptrace SIGSTOP:ped this
1834        //             status = (redox_rt::protocol::SIGSTOP << 8) | 0x7f;
1835        //             assert!(wifstopped(status));
1836        //             assert_eq!(wstopsig(status), redox_rt::protocol::SIGSTOP);
1837        //             res = Some(Ok(pid as usize));
1838        //         }
1839        //     }
1840        // }
1841
1842        // If ptrace didn't impact this waitpid, proceed *almost* as
1843        // normal: We still need to add WUNTRACED, but we only return
1844        // it if (and only if) a ptrace traceme was activated during
1845        // the wait.
1846        let res = res.unwrap_or_else(|| {
1847            loop {
1848                let res = inner(&mut status, options | WaitFlags::WUNTRACED);
1849
1850                // TODO: Also handle special PIDs here
1851                if !wifstopped(status)
1852                    || options.contains(WaitFlags::WUNTRACED)
1853                    || ptrace::is_traceme(pid)
1854                {
1855                    break res;
1856                }
1857            }
1858        });
1859
1860        // If stat_loc is non-null, set that and the return
1861        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        // YIELD on Redox is 20, which is SYS_ARCH_PRCTL on Linux
1887        (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        // let file_off = Self::lseek(fd, 0, SEEK_SET)?;
1903        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            // FIXME: andypython: SEEK_CUR, SEEK_END
1919            c => {
1920                log::warn!(
1921                    "Sys::relative_to_absolute_foffset: whence={whence} not yet implemented"
1922                );
1923                Ok((0, 0))
1924            }
1925        }
1926    }
1927}