diff --git a/.cargo-ok b/.cargo-ok new file mode 100644 index 0000000000..5f8b795830 --- /dev/null +++ b/.cargo-ok @@ -0,0 +1 @@ +{"v":1} \ No newline at end of file diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000000..16aee67720 --- /dev/null +++ b/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "7040cf71b3a5d15d91802810d0a50aa197970c43" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..4fffb2f89c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 75d5b5da72..c1d0e231cc 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -7,7 +7,7 @@ authors = ["4lDO2 <4lDO2@protonmail.com>"] # variants). The version field stays at upstream 0.1.17 # per the AGENTS.md "GOLDEN RULE" — periodic rebase via # `git fetch upstream` is the workflow when upstream changes. -version = "0.1.17" +version = "0.1.18" edition = "2021" license = "MIT" description = "Redox stable ABI" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..fec648fd0f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 4lDO2 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000..c36341029e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,1134 @@ +//! Redox-specific system library. +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "call")] +use self::error::{Error, Result}; + +#[cfg(feature = "base")] +pub mod error { + use super::*; + + #[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] + // TODO: Define as enum? + pub struct Error { + // TODO: NonZeroU16? "success" is not an error. + errno: u16, + } + + impl Error { + pub fn new(errno: i32) -> Self { + Self { + errno: errno.try_into().unwrap_or(u16::MAX), + } + } + pub fn errno(self) -> i32 { + self.errno.into() + } + pub fn is_wouldblock(self) -> bool { + matches!(self.errno(), errno::EAGAIN | errno::EWOULDBLOCK) + } + pub fn is_interrupt(self) -> bool { + self.errno() == errno::EINTR + } + pub fn mux(res: Result) -> usize { + match res { + // TODO: Ensure success cannot overlap with error values. + Ok(success) => success, + Err(error) => usize::wrapping_neg(usize::from(error.errno)), + } + } + pub fn demux(res: usize) -> Result { + if res > usize::wrapping_neg(4096) { + Err(Self { + errno: res.wrapping_neg().try_into().expect("2^BITS - res < 4096"), + }) + } else { + Ok(res) + } + } + #[cfg(feature = "call")] + pub fn description(self, buf: &mut [u8]) -> &str { + call::strerror(self.errno, buf).map_or("unknown error", |(desc, _)| desc) + } + } + + impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "call")] + { + write!(f, "{}", self.description(&mut [0; 256])) + } + #[cfg(not(feature = "call"))] + { + write!(f, "Error {} (strerror unavailable)", self.errno) + } + } + } + impl core::fmt::Debug for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "call")] + { + write!( + f, + "Error `{}` {}", + self.description(&mut [0; 256]), + self.errno + ) + } + #[cfg(not(feature = "call"))] + { + write!(f, "Error {} (strerror unavailable)", self.errno) + } + } + } + #[cfg(feature = "redox_syscall")] + impl From for Error { + fn from(value: syscall::Error) -> Self { + Self { + errno: value.errno.try_into().unwrap_or(u16::MAX), + } + } + } + #[cfg(feature = "redox_syscall")] + impl From for syscall::Error { + fn from(value: Error) -> Self { + Self::new(value.errno()) + } + } + #[cfg(feature = "std")] + impl From for std::io::Error { + fn from(value: Error) -> Self { + Self::from_raw_os_error(value.errno.into()) + } + } + + #[cfg(feature = "std")] + impl std::error::Error for Error {} + + pub type Result = core::result::Result; +} + +#[cfg(feature = "base")] +pub mod flag { + pub use libc::{ + O_ACCMODE, O_APPEND, O_ASYNC, O_CLOEXEC, O_CREAT, O_DIRECTORY, O_EXCL, O_FSYNC, O_NOFOLLOW, + O_NONBLOCK, O_PATH, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, + }; + + pub use libc::{MSG_DONTROUTE, MSG_DONTWAIT, MSG_EOR, MSG_OOB, MSG_PEEK}; + + pub use libc::{CLOCK_MONOTONIC, CLOCK_REALTIME}; + + pub use libc::{SIG_BLOCK, SIG_SETMASK, SIG_UNBLOCK}; + + pub use libc::{ + SIGABRT, + SIGALRM, + SIGBUS, + SIGCHLD, + SIGCONT, + SIGFPE, + // TODO: Const rather than use, to convert to u32 + SIGHUP, + SIGILL, + SIGINT, + SIGIO, + SIGKILL, + SIGPIPE, + SIGPROF, + SIGPWR, + SIGQUIT, + SIGSEGV, + SIGSTKFLT, + SIGSYS, + SIGTERM, + SIGTRAP, + SIGTSTP, + SIGTTIN, + SIGTTOU, + SIGURG, + SIGUSR1, + SIGUSR2, + SIGVTALRM, + SIGWINCH, + SIGXFSZ, + }; + + #[cfg(target_os = "redox")] + pub use libc::{O_EXLOCK, O_SHLOCK, O_SYMLINK}; + + pub const MAP_SHARED: u32 = libc::MAP_SHARED as u32; + pub const MAP_PRIVATE: u32 = libc::MAP_PRIVATE as u32; + + pub const PROT_NONE: u32 = libc::PROT_NONE as u32; + pub const PROT_READ: u32 = libc::PROT_READ as u32; + pub const PROT_WRITE: u32 = libc::PROT_WRITE as u32; + pub const PROT_EXEC: u32 = libc::PROT_EXEC as u32; + + pub const WNOHANG: u32 = libc::WNOHANG as u32; + pub const WUNTRACED: u32 = libc::WUNTRACED as u32; + pub const WCONTINUED: u32 = libc::WCONTINUED as u32; + + // CLOEXEC flags (added in libredox 0.1.18) + pub const O_CLOEXEC: usize = 0x0100_0000; + pub const F_DUPFD_CLOEXEC: usize = 1030; +} + +/// Red Bear OS Phase J (re-applied for libredox 0.1.18): +/// Re-exports the ACPI verb enum from the local `redox_syscall` fork +/// so that `acpid`, `kstop`, and other Red Bear OS userspace tools +/// (e.g. `redbear-power`, `redbear-acmd`) can address the Red Bear OS +/// extended ACPI verbs (EnterS2Idle / ExitS2Idle / SetS3WakingVector / +/// EnterS3) by symbolic name instead of hardcoding the integer +/// discriminants. Mirrors the same pattern that upstream libredox +/// uses for `libc::EXIT_SUCCESS`, `libc::SIGTERM`, etc. +/// +/// Without this re-export, any code that wanted to send +/// `AcPiVerb::EnterS2Idle` to the kernel via the `kcall` proc-scheme +/// handler would have to write `3u64` (the raw discriminant) and hope +/// the kernel's enum didn't reorder. With this re-export, the +/// discriminant is sourced from the local `redox_syscall` fork, +/// which is the same source the kernel uses to interpret the verb, +/// so reorderings are caught at compile time. +#[cfg(feature = "redox_syscall")] +pub mod acpi { + pub use redox_syscall::AcPiVerb; +} + +#[cfg(feature = "base")] +pub mod errno { + pub use libc::{ + E2BIG, EACCES, EADDRINUSE, EADDRNOTAVAIL, EADV, EAFNOSUPPORT, EAGAIN, EALREADY, EBADE, + EBADF, EBADFD, EBADMSG, EBADR, EBADRQC, EBADSLT, EBFONT, EBUSY, ECANCELED, ECHILD, ECHRNG, + ECOMM, ECONNABORTED, ECONNREFUSED, ECONNRESET, EDEADLK, EDEADLOCK, EDESTADDRREQ, EDOM, + EDOTDOT, EDQUOT, EEXIST, EFAULT, EFBIG, EHOSTDOWN, EHOSTUNREACH, EIDRM, EILSEQ, + EINPROGRESS, EINTR, EINVAL, EIO, EISCONN, EISDIR, EISNAM, EKEYEXPIRED, EKEYREJECTED, + EKEYREVOKED, EL2HLT, EL2NSYNC, EL3HLT, EL3RST, ELIBACC, ELIBBAD, ELIBEXEC, ELIBMAX, + ELIBSCN, ELNRNG, ELOOP, EMEDIUMTYPE, EMFILE, EMLINK, EMSGSIZE, EMULTIHOP, ENAMETOOLONG, + ENAVAIL, ENETDOWN, ENETRESET, ENETUNREACH, ENFILE, ENOANO, ENOBUFS, ENOCSI, ENODEV, ENOENT, + ENOEXEC, ENOKEY, ENOLCK, ENOMEDIUM, ENOMEM, ENOMSG, ENONET, ENOPKG, ENOPROTOOPT, ENOSPC, + ENOSR, ENOSTR, ENOSYS, ENOTBLK, ENOTCONN, ENOTDIR, ENOTEMPTY, ENOTNAM, ENOTRECOVERABLE, + ENOTSOCK, ENOTTY, ENOTUNIQ, ENXIO, EOPNOTSUPP, EOVERFLOW, EOWNERDEAD, EPERM, EPFNOSUPPORT, + EPIPE, EPROTO, EPROTONOSUPPORT, EPROTOTYPE, ERANGE, EREMCHG, EREMOTE, EREMOTEIO, ERESTART, + EROFS, ESHUTDOWN, ESOCKTNOSUPPORT, ESPIPE, ESRCH, ESRMNT, ESTALE, ESTRPIPE, ETIME, + ETIMEDOUT, ETOOMANYREFS, ETXTBSY, EUCLEAN, EUNATCH, EUSERS, EWOULDBLOCK, EXDEV, EXFULL, + }; +} +#[cfg(feature = "base")] +pub mod data { + pub use libc::iovec as IoVec; + pub use libc::sigaction as SigAction; + pub use libc::stat as Stat; + pub use libc::statvfs as StatVfs; + pub use libc::timespec as TimeSpec; + + // TODO: Remove + pub fn timespec_from_mut_bytes(bytes: &mut [u8]) -> &mut TimeSpec { + assert!(bytes.len() >= core::mem::size_of::()); + assert_eq!( + bytes.as_ptr() as usize % core::mem::align_of::(), + 0 + ); + unsafe { &mut *bytes.as_mut_ptr().cast() } + } + // TODO: Remove + pub fn timespec_from_bytes(bytes: &[u8]) -> &TimeSpec { + assert!(bytes.len() >= core::mem::size_of::()); + assert_eq!( + bytes.as_ptr() as usize % core::mem::align_of::(), + 0 + ); + unsafe { &*bytes.as_ptr().cast() } + } + + #[cfg(target_os = "redox")] + pub use libc::sigset_t as SigSet; + + // TODO: Should libredox compile on non-Redox platforms? + #[cfg(not(target_os = "redox"))] + pub type SigSet = u64; +} + +#[cfg(feature = "call")] +type RawResult = usize; + +#[cfg(feature = "call")] +#[allow(dead_code)] +extern "C" { + // NOTE: Although there are version suffixes, there'd have to be strong reasons for adding new + // version. + fn redox_open_v1(path_base: *const u8, path_len: usize, flags: u32, mode: u16) -> RawResult; + fn redox_openat_v1( + fd: usize, + buf: *const u8, + path_len: usize, + flags: u32, + fcntl_flags: u32, + ) -> RawResult; + fn redox_dup_v1(fd: usize, buf: *const u8, len: usize) -> RawResult; + fn redox_dup2_v1(old_fd: usize, new_fd: usize, buf: *const u8, len: usize) -> RawResult; + fn redox_read_v1(fd: usize, dst_base: *mut u8, dst_len: usize) -> RawResult; + fn redox_write_v1(fd: usize, src_base: *const u8, src_len: usize) -> RawResult; + fn redox_fchmod_v1(fd: usize, new_mode: u16) -> RawResult; + fn redox_fchown_v1(fd: usize, new_uid: u32, new_gid: u32) -> RawResult; + fn redox_getdents_v0(fd: usize, buf: *mut u8, buf_len: usize, opaque: u64) -> RawResult; + fn redox_fstat_v1(fd: usize, dst: *mut data::Stat) -> RawResult; + fn redox_fstatvfs_v1(fd: usize, dst: *mut data::StatVfs) -> RawResult; + fn redox_fsync_v1(fd: usize) -> RawResult; + fn redox_fdatasync_v1(fd: usize) -> RawResult; + fn redox_ftruncate_v0(fd: usize, len: usize) -> RawResult; + fn redox_futimens_v1(fd: usize, times: *const data::TimeSpec) -> RawResult; + /* TODO: Support unlinkat using std_fs_call + fn redox_unlinkat_v0(fd: usize, buf: *const u8, path_len: usize, flags: u32) -> RawResult; + */ + fn redox_fpath_v1(fd: usize, dst_base: *mut u8, dst_len: usize) -> RawResult; + fn redox_relpathat_v0(dirfd: usize, fd: usize, dst_base: *mut u8, dst_len: usize) -> RawResult; + fn redox_close_v1(fd: usize) -> RawResult; + + // Added in libredox 0.1.18 to match upstream. Required by + // downstream recipes (redoxfs) that depend on libredox 0.1.18: + // the local [patch.crates-io] override forces this fork's + // version to be used in the kernel compilation, so this + // extern must match what upstream 0.1.18 exports. + fn redox_fcntl_v0(fd: usize, cmd: usize, arg: usize) -> RawResult; + + // NOTE: While the Redox kernel currently doesn't distinguish between threads and processes, + // the return value of this function is expected to be treated as a process ID and not a thread + // ID. + fn redox_get_pid_v1() -> RawResult; + + fn redox_get_euid_v1() -> RawResult; + fn redox_get_ruid_v1() -> RawResult; + fn redox_get_egid_v1() -> RawResult; + fn redox_get_rgid_v1() -> RawResult; + + fn redox_get_ens_v0() -> RawResult; + fn redox_get_ns_v0() -> RawResult; + // This function is used to get the credentials, pid, euid, egid etc. of the process with the target pid. + fn redox_get_proc_credentials_v1(cap_fd: usize, target_pid: usize, buf: &mut [u8]) + -> RawResult; + + fn redox_setrens_v1(rns: usize, ens: usize) -> RawResult; + fn redox_mkns_v1(names: *const data::IoVec, num_names: usize, _flags: u32) -> RawResult; + + fn redox_kill_v1(pid: usize, signal: u32) -> RawResult; + fn redox_waitpid_v1(pid: usize, status: *mut i32, options: u32) -> RawResult; + + fn redox_sigprocmask_v1(how: u32, new: *const u64, old: *mut u64) -> RawResult; + fn redox_sigaction_v1( + signal: u32, + new: *const data::SigAction, + old: *mut data::SigAction, + ) -> RawResult; + + fn redox_clock_gettime_v1(clock: usize, ts: *mut data::TimeSpec) -> RawResult; + + fn redox_mmap_v1( + addr: *mut (), + unaligned_len: usize, + prot: u32, + flags: u32, + fd: usize, + offset: u64, + ) -> RawResult; + fn redox_munmap_v1(addr: *mut (), unaligned_len: usize) -> RawResult; + + fn redox_strerror_v1(dst: *mut u8, dst_len: *mut usize, error: u32) -> RawResult; + + fn redox_sys_call_v0( + fd: usize, + payload: *mut u8, + payload_len: usize, + flags: usize, + metadata: *const u64, + metadata_len: usize, + ) -> RawResult; + fn redox_get_socket_token_v0(fd: usize, payload: *mut u8, payload_len: usize) -> RawResult; + + fn redox_setns_v0(fd: usize) -> RawResult; + + fn redox_register_scheme_to_ns_v0( + ns_fd: usize, + name_base: *const u8, + name_len: usize, + cap_fd: usize, + ) -> RawResult; +} + +#[cfg(feature = "call")] +pub struct Fd(usize); + +#[cfg(feature = "call")] +impl Fd { + pub fn new(raw: usize) -> Self { + Self(raw) + } + #[inline] + pub fn open(path: &str, flags: i32, mode: u16) -> Result { + Ok(Self(call::open(path, flags, mode)?)) + } + #[inline] + pub fn openat(&self, path: &str, flags: i32, fcntl_flags: u32) -> Result { + Ok(Self(call::openat(self.raw(), path, flags, fcntl_flags)?)) + } + /// Added in libredox 0.1.18 to match upstream. Performs an + /// `fcntl(2)` operation on this file descriptor. The `cmd` and + /// `arg` arguments are passed through to the kernel unchanged. + #[inline] + pub fn fcntl(&self, cmd: usize, arg: usize) -> Result { + call::fcntl(self.raw(), cmd, arg) + } + #[inline] + pub fn dup(&self, buf: &[u8]) -> Result { + call::dup(self.raw(), buf) + } + #[inline] + pub fn dup2(&self, new_fd: usize, buf: &[u8]) -> Result { + call::dup2(self.raw(), new_fd, buf) + } + + #[inline] + pub const fn raw(&self) -> usize { + self.0 + } + + #[inline] + pub fn into_raw(self) -> usize { + let raw = self.raw(); + core::mem::forget(self); + raw + } + + #[inline] + pub fn read(&self, buf: &mut [u8]) -> Result { + call::read(self.raw(), buf) + } + #[inline] + pub fn write(&self, buf: &[u8]) -> Result { + call::write(self.raw(), buf) + } + #[inline] + pub fn chmod(&self, new_mode: u16) -> Result<()> { + call::fchmod(self.raw(), new_mode) + } + #[inline] + pub fn chown(&self, new_uid: u32, new_gid: u32) -> Result<()> { + call::fchown(self.raw(), new_uid, new_gid) + } + #[inline] + pub fn getdents(self, buf: &mut [u8], opaque: u64) -> Result { + call::getdents(self.raw(), buf, opaque) + } + pub fn stat(&self) -> Result { + call::fstat(self.raw()) + } + pub fn statvfs(&self) -> Result { + call::fstatvfs(self.raw()) + } + #[inline] + pub fn fsync(&self) -> Result<()> { + call::fsync(self.raw()) + } + #[inline] + pub fn fdatasync(&self) -> Result<()> { + call::fdatasync(self.raw()) + } + #[inline] + pub fn ftruncate(self, len: usize) -> Result<()> { + call::ftruncate(self.raw(), len) + } + #[inline] + pub fn futimens(self, times: &[data::TimeSpec; 2]) -> Result<()> { + call::futimens(self.raw(), times) + } + /* TODO: Support unlinkat using std_fs_call + #[inline] + pub fn unlinkat(&self, path: &str, flags: i32) -> Result<()> { + call::unlinkat(self.raw(), path, flags) + } + */ + #[inline] + pub fn fpath(&self, path: &mut [u8]) -> Result { + call::fpath(self.raw(), path) + } + #[inline] + pub fn relpathat(&self, fd: usize, path: &mut [u8]) -> Result { + call::relpathat(self.raw(), fd, path) + } + #[inline] + pub fn close(self) -> Result<()> { + call::close(self.into_raw()) + } + + #[cfg(feature = "redox_syscall")] + #[inline] + pub fn call_ro( + &self, + payload: &mut [u8], + flags: syscall::CallFlags, + metadata: &[u64], + ) -> Result { + call::call_ro(self.raw(), payload, flags, metadata) + } + #[cfg(feature = "redox_syscall")] + #[inline] + pub fn call_wo( + &self, + payload: &[u8], + flags: syscall::CallFlags, + metadata: &[u64], + ) -> Result { + call::call_wo(self.raw(), payload, flags, metadata) + } + #[cfg(feature = "redox_syscall")] + #[inline] + pub fn call_rw( + &self, + payload: &mut [u8], + flags: syscall::CallFlags, + metadata: &[u64], + ) -> Result { + call::call_rw(self.raw(), payload, flags, metadata) + } +} +#[cfg(feature = "call")] +impl Drop for Fd { + fn drop(&mut self) { + let _ = unsafe { redox_close_v1(self.0) }; + } +} + +#[cfg(feature = "call")] +pub mod call { + use core::mem::MaybeUninit; + + use super::*; + + /// flags and mode are binary compatible with libc + #[inline] + pub fn open(path: impl AsRef, flags: i32, mode: u16) -> Result { + let path = path.as_ref(); + Ok(Error::demux(unsafe { + redox_open_v1(path.as_ptr(), path.len(), flags as u32, mode) + })?) + } + #[inline] + pub fn openat( + fd: usize, + path: impl AsRef<[u8]>, + flags: i32, + fcntl_flags: u32, + ) -> Result { + let path = path.as_ref(); + Ok(Error::demux(unsafe { + redox_openat_v1(fd, path.as_ptr(), path.len(), flags as u32, fcntl_flags) + })?) + } + /// Added in libredox 0.1.18 to match upstream. Performs an + /// `fcntl(2)` operation on a raw file descriptor. + #[inline] + pub fn fcntl(fd: usize, cmd: usize, arg: usize) -> Result { + Error::demux(unsafe { redox_fcntl_v0(fd, cmd, arg) }) + } + #[inline] + pub fn dup(fd: usize, buf: impl AsRef<[u8]>) -> Result { + let buf = buf.as_ref(); + Ok(Error::demux(unsafe { + redox_dup_v1(fd, buf.as_ptr(), buf.len()) + })?) + } + #[inline] + pub fn dup2(old_fd: usize, new_fd: usize, buf: impl AsRef<[u8]>) -> Result { + let buf = buf.as_ref(); + Ok(Error::demux(unsafe { + redox_dup2_v1(old_fd, new_fd, buf.as_ptr(), buf.len()) + })?) + } + #[inline] + pub fn read(raw_fd: usize, buf: &mut [u8]) -> Result { + Ok(Error::demux(unsafe { + redox_read_v1(raw_fd, buf.as_mut_ptr(), buf.len()) + })?) + } + #[inline] + pub fn write(raw_fd: usize, buf: &[u8]) -> Result { + Error::demux(unsafe { redox_write_v1(raw_fd, buf.as_ptr(), buf.len()) }) + } + #[inline] + pub fn fchmod(raw_fd: usize, new_mode: u16) -> Result<()> { + Error::demux(unsafe { redox_fchmod_v1(raw_fd, new_mode) })?; + Ok(()) + } + #[inline] + pub fn fchown(raw_fd: usize, new_uid: u32, new_gid: u32) -> Result<()> { + Error::demux(unsafe { redox_fchown_v1(raw_fd, new_uid, new_gid) })?; + Ok(()) + } + #[inline] + pub fn getdents(fd: usize, buf: &mut [u8], opaque: u64) -> Result { + Error::demux(unsafe { redox_getdents_v0(fd, buf.as_mut_ptr(), buf.len(), opaque) }) + } + #[inline] + pub fn fstat(raw_fd: usize) -> Result { + unsafe { + let mut ret = MaybeUninit::uninit(); + Error::demux(redox_fstat_v1(raw_fd, ret.as_mut_ptr()))?; + Ok(ret.assume_init()) + } + } + #[inline] + pub fn fstatvfs(raw_fd: usize) -> Result { + unsafe { + let mut ret = MaybeUninit::uninit(); + Error::demux(redox_fstatvfs_v1(raw_fd, ret.as_mut_ptr()))?; + Ok(ret.assume_init()) + } + } + #[inline] + pub fn fsync(raw_fd: usize) -> Result<()> { + Error::demux(unsafe { redox_fsync_v1(raw_fd) }).map(|_| ()) + } + #[inline] + pub fn fdatasync(raw_fd: usize) -> Result<()> { + Error::demux(unsafe { redox_fdatasync_v1(raw_fd) }).map(|_| ()) + } + #[inline] + pub fn ftruncate(raw_fd: usize, new_size: usize) -> Result<()> { + Error::demux(unsafe { redox_ftruncate_v0(raw_fd, new_size) }).map(|_| ()) + } + #[inline] + pub fn futimens(raw_fd: usize, times: &[data::TimeSpec; 2]) -> Result<()> { + Error::demux(unsafe { redox_futimens_v1(raw_fd, times.as_ptr()) })?; + Ok(()) + } + /* TODO: Support unlinkat using std_fs_call + #[inline] + pub fn unlinkat(fd: usize, path: impl AsRef<[u8]>, flags: i32) -> Result<()> { + let path = path.as_ref(); + Error::demux(unsafe { redox_unlinkat_v0(fd, path.as_ptr(), path.len(), flags as u32) }) + .map(|_| ()) + } + */ + #[inline] + pub fn fpath(raw_fd: usize, buf: &mut [u8]) -> Result { + Error::demux(unsafe { redox_fpath_v1(raw_fd, buf.as_mut_ptr(), buf.len()) }) + } + #[inline] + pub fn relpathat(raw_fd: usize, fd: usize, buf: &mut [u8]) -> Result { + Error::demux(unsafe { redox_relpathat_v0(raw_fd, fd, buf.as_mut_ptr(), buf.len()) }) + } + #[inline] + pub fn close(raw_fd: usize) -> Result<()> { + Error::demux(unsafe { redox_close_v1(raw_fd) })?; + Ok(()) + } + #[cfg(feature = "redox_syscall")] + #[inline] + pub fn call_ro( + fd: usize, + payload: &mut [u8], + flags: syscall::CallFlags, + metadata: &[u64], + ) -> Result { + Ok(Error::demux(unsafe { + redox_sys_call_v0( + fd, + payload.as_mut_ptr(), + payload.len(), + (flags | syscall::CallFlags::READ).bits(), + metadata.as_ptr(), + metadata.len(), + ) + })?) + } + #[cfg(feature = "redox_syscall")] + #[inline] + pub fn call_wo( + fd: usize, + payload: &[u8], + flags: syscall::CallFlags, + metadata: &[u64], + ) -> Result { + Ok(Error::demux(unsafe { + redox_sys_call_v0( + fd, + payload.as_ptr() as *mut u8, + payload.len(), + (flags | syscall::CallFlags::WRITE).bits(), + metadata.as_ptr(), + metadata.len(), + ) + })?) + } + #[cfg(feature = "redox_syscall")] + #[inline] + pub fn call_rw( + fd: usize, + payload: &mut [u8], + flags: syscall::CallFlags, + metadata: &[u64], + ) -> Result { + Ok(Error::demux(unsafe { + redox_sys_call_v0( + fd, + payload.as_mut_ptr(), + payload.len(), + (flags | syscall::CallFlags::READ | syscall::CallFlags::WRITE).bits(), + metadata.as_ptr(), + metadata.len(), + ) + })?) + } + + #[inline] + pub fn geteuid() -> Result { + Error::demux(unsafe { redox_get_euid_v1() }) + } + + #[inline] + pub fn getruid() -> Result { + Error::demux(unsafe { redox_get_ruid_v1() }) + } + + #[inline] + pub fn getegid() -> Result { + Error::demux(unsafe { redox_get_egid_v1() }) + } + #[inline] + pub fn getrgid() -> Result { + Error::demux(unsafe { redox_get_rgid_v1() }) + } + #[inline] + pub fn getpid() -> Result { + Error::demux(unsafe { redox_get_pid_v1() }) + } + #[inline] + pub fn getens() -> Result { + Error::demux(unsafe { redox_get_ens_v0() }) + } + + #[inline] + // [u8; size_of::()] + pub fn get_proc_credentials(cap_fd: usize, target_pid: usize, buf: &mut [u8]) -> Result { + Error::demux(unsafe { redox_get_proc_credentials_v1(cap_fd, target_pid, buf) }) + } + + #[inline] + pub fn setrens(rns: usize, ens: usize) -> Result { + Error::demux(unsafe { redox_setrens_v1(rns, ens) }) + } + #[inline] + pub fn waitpid(pid: usize, status: &mut i32, options: i32) -> Result { + Error::demux(unsafe { redox_waitpid_v1(pid, status as *mut i32, options as u32) }) + } + #[inline] + pub fn kill(pid: usize, signal: u32) -> Result<()> { + Error::demux(unsafe { redox_kill_v1(pid, signal) }).map(|_| ()) + } + #[inline] + pub fn clock_gettime(clock: i32) -> Result { + unsafe { + let mut ret = MaybeUninit::uninit(); + Error::demux(redox_clock_gettime_v1(clock as usize, ret.as_mut_ptr()))?; + Ok(ret.assume_init()) + } + } + #[inline] + pub fn sigprocmask( + how: i32, + newmask: Option<&data::SigSet>, + oldmask: Option<&mut data::SigSet>, + ) -> Result<()> { + Error::demux(unsafe { + redox_sigprocmask_v1( + how as u32, + newmask.map_or(core::ptr::null(), |m| m), + oldmask.map_or(core::ptr::null_mut(), |m| m), + ) + }) + .map(|_| ()) + } + #[inline] + pub fn sigaction( + signal: i32, + newact: Option<&data::SigAction>, + oldact: Option<&mut data::SigAction>, + ) -> Result<()> { + Error::demux(unsafe { + redox_sigaction_v1( + signal as u32, + newact.map_or(core::ptr::null(), |m| m), + oldact.map_or(core::ptr::null_mut(), |m| m), + ) + }) + .map(|_| ()) + } + + #[derive(Clone, Copy, Debug)] + pub struct MmapArgs { + pub addr: *mut (), + pub length: usize, + pub prot: u32, + pub flags: u32, + pub fd: usize, + pub offset: u64, + } + #[inline] + pub unsafe fn mmap(args: MmapArgs) -> Result<*mut ()> { + Error::demux(redox_mmap_v1( + args.addr, + args.length, + args.prot, + args.flags, + args.fd, + args.offset, + )) + .map(|addr| addr as *mut ()) + } + #[inline] + pub unsafe fn munmap(addr: *mut (), length: usize) -> Result<()> { + Error::demux(redox_munmap_v1(addr, length)).map(|_| ()) + } + + #[inline] + pub fn strerror(error: u16, desc: &mut [u8]) -> Option<(&str, usize)> { + unsafe { + let mut len_inout = desc.len(); + let copied_len = Error::demux(redox_strerror_v1( + desc.as_mut_ptr(), + &mut len_inout, + error.into(), + )) + .ok()?; + Some(( + core::str::from_utf8_unchecked(&desc[..copied_len]), + len_inout, + )) + } + } + + #[inline] + #[cfg(feature = "mkns")] + pub fn mkns(names: &[ioslice::IoSlice]) -> Result { + // no-op + let iovecs = ioslice::IoSlice::cast_to_raw_iovecs(names); + + unsafe { Error::demux(redox_mkns_v1(iovecs.as_ptr(), iovecs.len(), 0)) } + } + + #[inline] + pub fn get_socket_token(fd: usize, buf: &mut [u8]) -> Result { + Error::demux(unsafe { redox_get_socket_token_v0(fd, buf.as_mut_ptr(), buf.len()) }) + } + + #[inline] + pub fn setns(fd: usize) -> Result { + Error::demux(unsafe { redox_setns_v0(fd) }) + } + #[inline] + pub fn getns() -> Result { + Error::demux(unsafe { redox_get_ns_v0() }) + } + + #[inline] + pub fn register_scheme_to_ns(ns_fd: usize, name: impl AsRef, cap_fd: usize) -> Result<()> { + let name = name.as_ref(); + Error::demux(unsafe { + redox_register_scheme_to_ns_v0(ns_fd, name.as_ptr(), name.len(), cap_fd) + }) + .map(|_| ()) + } +} + +#[cfg(feature = "protocol")] +pub mod protocol { + use bitflags::bitflags; + + #[derive(Clone, Copy, Debug, Default)] + #[repr(C)] + pub struct ProcMeta { + pub pid: u32, + pub pgid: u32, + pub ppid: u32, + pub ruid: u32, + pub euid: u32, + pub suid: u32, + pub rgid: u32, + pub egid: u32, + pub sgid: u32, + pub ens: u32, + pub rns: u32, + } + unsafe impl plain::Plain for ProcMeta {} + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(usize)] + pub enum ProcCall { + Waitpid = 0, + Setrens = 1, + Exit = 2, + Waitpgid = 3, + SetResugid = 4, + Setpgid = 5, + Getsid = 6, + Setsid = 7, + Kill = 8, + Sigq = 9, + + // TODO: replace with sendfd equivalent syscall for sending memory + SyncSigPctl = 10, + Sigdeq = 11, + Getppid = 12, + Rename = 13, + DisableSetpgid = 14, + + // Temporary calls for getting process credentials + GetProcCredentials = 15, + + SetProcPriority = 16, + GetProcPriority = 17, + } + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(usize)] + pub enum ThreadCall { + // TODO: replace with sendfd equivalent syscall for sending memory, or force userspace to + // obtain its TCB memory from this server + SyncSigTctl = 0, + SignalThread = 1, + } + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(usize)] + #[non_exhaustive] + pub enum SocketCall { + Bind = 0, + Connect = 1, + SetSockOpt = 2, + GetSockOpt = 3, + SendMsg = 4, + RecvMsg = 5, + Unbind = 6, + GetToken = 7, + GetPeerName = 8, + Shutdown = 9, + } + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(usize)] + #[non_exhaustive] + pub enum FsCall { + Connect = 0, + } + + impl ProcCall { + pub fn try_from_raw(raw: usize) -> Option { + Some(match raw { + 0 => Self::Waitpid, + 1 => Self::Setrens, + 2 => Self::Exit, + 3 => Self::Waitpgid, + 4 => Self::SetResugid, + 5 => Self::Setpgid, + 6 => Self::Getsid, + 7 => Self::Setsid, + 8 => Self::Kill, + 9 => Self::Sigq, + 10 => Self::SyncSigPctl, + 11 => Self::Sigdeq, + 12 => Self::Getppid, + 13 => Self::Rename, + 14 => Self::DisableSetpgid, + 15 => Self::GetProcCredentials, + 16 => Self::SetProcPriority, + 17 => Self::GetProcPriority, + _ => return None, + }) + } + } + impl ThreadCall { + pub fn try_from_raw(raw: usize) -> Option { + Some(match raw { + 0 => Self::SyncSigTctl, + 1 => Self::SignalThread, + _ => return None, + }) + } + } + + impl SocketCall { + pub fn try_from_raw(raw: usize) -> Option { + Some(match raw { + 0 => Self::Bind, + 1 => Self::Connect, + 2 => Self::SetSockOpt, + 3 => Self::GetSockOpt, + 4 => Self::SendMsg, + 5 => Self::RecvMsg, + 6 => Self::Unbind, + 7 => Self::GetToken, + 8 => Self::GetPeerName, + 9 => Self::Shutdown, + _ => return None, + }) + } + } + + impl FsCall { + pub fn try_from_raw(raw: usize) -> Option { + Some(match raw { + 0 => Self::Connect, + _ => return None, + }) + } + } + + bitflags! { + #[derive(Clone, Copy, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] + pub struct WaitFlags: usize { + const WNOHANG = 0x01; + const WUNTRACED = 0x02; + const WCONTINUED = 0x08; + } + } + /// True if status indicates the child is stopped. + pub fn wifstopped(status: usize) -> bool { + (status & 0xff) == 0x7f + } + + /// If wifstopped(status), the signal that stopped the child. + pub fn wstopsig(status: usize) -> usize { + (status >> 8) & 0xff + } + + /// True if status indicates the child continued after a stop. + pub fn wifcontinued(status: usize) -> bool { + status == 0xffff + } + + /// True if STATUS indicates termination by a signal. + pub fn wifsignaled(status: usize) -> bool { + ((status & 0x7f) + 1) as i8 >= 2 + } + + /// If wifsignaled(status), the terminating signal. + pub fn wtermsig(status: usize) -> usize { + status & 0x7f + } + + /// True if status indicates normal termination. + pub fn wifexited(status: usize) -> bool { + wtermsig(status) == 0 + } + + /// If wifexited(status), the exit status. + pub fn wexitstatus(status: usize) -> usize { + (status >> 8) & 0xff + } + + /// True if status indicates a core dump was created. + pub fn wcoredump(status: usize) -> bool { + (status & 0x80) != 0 + } + #[derive(Clone, Copy, Debug)] + pub enum ProcKillTarget { + ThisGroup, + SingleProc(usize), + ProcGroup(usize), + All, + } + impl ProcKillTarget { + pub fn raw(self) -> usize { + match self { + Self::ThisGroup => 0, + Self::SingleProc(p) => p, + Self::ProcGroup(g) => usize::wrapping_neg(g), + Self::All => usize::wrapping_neg(1), + } + } + pub fn from_raw(raw: usize) -> Self { + let raw = raw as isize; + if raw == 0 { + Self::ThisGroup + } else if raw == -1 { + Self::All + } else if raw < 0 { + Self::ProcGroup(raw.wrapping_neg() as usize) + } else { + Self::SingleProc(raw as usize) + } + } + } + #[derive(Copy, Clone, Debug, Default, PartialEq)] + #[repr(C)] + pub struct RtSigInfo { + pub arg: usize, + pub code: i32, + pub uid: u32, + pub pid: u32, // TODO: usize? + } + unsafe impl plain::Plain for RtSigInfo {} + + pub const SIGHUP: usize = 1; + pub const SIGINT: usize = 2; + pub const SIGQUIT: usize = 3; + pub const SIGILL: usize = 4; + pub const SIGTRAP: usize = 5; + pub const SIGABRT: usize = 6; + pub const SIGBUS: usize = 7; + pub const SIGFPE: usize = 8; + pub const SIGKILL: usize = 9; + pub const SIGUSR1: usize = 10; + pub const SIGSEGV: usize = 11; + pub const SIGUSR2: usize = 12; + pub const SIGPIPE: usize = 13; + pub const SIGALRM: usize = 14; + pub const SIGTERM: usize = 15; + pub const SIGSTKFLT: usize = 16; + pub const SIGCHLD: usize = syscall::SIGCHLD; + pub const SIGCONT: usize = 18; + pub const SIGSTOP: usize = 19; + pub const SIGTSTP: usize = syscall::SIGTSTP; + pub const SIGTTIN: usize = syscall::SIGTTIN; + pub const SIGTTOU: usize = syscall::SIGTTOU; + pub const SIGURG: usize = 23; + pub const SIGXCPU: usize = 24; + pub const SIGXFSZ: usize = 25; + pub const SIGVTALRM: usize = 26; + pub const SIGPROF: usize = 27; + pub const SIGWINCH: usize = 28; + pub const SIGIO: usize = 29; + pub const SIGPWR: usize = 30; + pub const SIGSYS: usize = 31; + + bitflags! { + #[derive(Clone, Copy, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] + pub struct NsPermissions: usize { + /// List schemes in the namespace + const LIST = 1 << 0; + /// Register a new scheme in the namespace + const INSERT = 1 << 1; + /// Delete a scheme from the namespace + const DELETE = 1 << 2; + /// Get scheme creation capabilities of the namespace + const SCHEME_CREATE = 1 << 3; + } + } + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(usize)] + pub enum NsDup { + ForkNs = 0, + ShrinkPermissions = 1, + IssueRegister = 2, + } + impl NsDup { + pub fn try_from_raw(raw: usize) -> Option { + Some(match raw { + 0 => Self::ForkNs, + 1 => Self::ShrinkPermissions, + 2 => Self::IssueRegister, + _ => return None, + }) + } + } +}