From 17f21113a06b459aeb499fe30956cb9d82a5edb3 Mon Sep 17 00:00:00 2001 From: Ibuki Omatsu Date: Fri, 27 Feb 2026 15:37:26 +0000 Subject: [PATCH] feat: Move redox-rt::protocol into libredox::protocol. Add getdents and futimens. --- Cargo.toml | 15 ++- src/lib.rs | 380 +++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 364 insertions(+), 31 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index aff99a02b4..c5bd4f3429 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "libredox" authors = ["4lDO2 <4lDO2@protonmail.com>"] -version = "0.1.12" +version = "0.1.13" edition = "2021" license = "MIT" description = "Redox stable ABI" @@ -11,13 +11,16 @@ exclude = ["target"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["call", "std", "redox_syscall"] -call = [] -std = [] +default = ["base", "call", "std", "redox_syscall", "protocol"] +base = [] +call = ["base", "libc"] +std = ["base", "libc"] +protocol = ["plain", "bitflags", "redox_syscall"] mkns = ["ioslice"] [dependencies] -bitflags = "2" -libc = "0.2" +bitflags = { version = "2", optional = true } +libc = { version = "0.2", optional = true } redox_syscall = { version = "0.7", optional = true } ioslice = { version = "0.6", optional = true } +plain = { version = "0.2", optional = true } diff --git a/src/lib.rs b/src/lib.rs index 025c1e2a13..9738c7ec29 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ #[cfg(feature = "call")] use self::error::{Error, Result}; +#[cfg(feature = "base")] pub mod error { use super::*; @@ -107,6 +108,7 @@ pub mod 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, @@ -166,6 +168,7 @@ pub mod flag { pub const WCONTINUED: u32 = libc::WCONTINUED as u32; } +#[cfg(feature = "base")] pub mod errno { pub use libc::{ E2BIG, EACCES, EADDRINUSE, EADDRNOTAVAIL, EADV, EAFNOSUPPORT, EAGAIN, EALREADY, EBADE, @@ -184,6 +187,7 @@ pub mod errno { 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; @@ -238,14 +242,19 @@ extern "C" { 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_fsync_v1(fd: usize) -> RawResult; - fn redox_fdatasync_v1(fd: 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_fpath_v1(fd: usize, dst_base: *mut u8, dst_len: usize) -> 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_close_v1(fd: usize) -> RawResult; // NOTE: While the Redox kernel currently doesn't distinguish between threads and processes, @@ -323,6 +332,7 @@ impl Fd { 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)?)) } @@ -356,8 +366,22 @@ impl Fd { call::write(self.raw(), buf) } #[inline] - pub fn fpath(&self, path: &mut [u8]) -> Result { - call::fpath(self.raw(), path) + 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<()> { @@ -367,22 +391,24 @@ impl Fd { pub fn fdatasync(&self) -> Result<()> { call::fdatasync(self.raw()) } - #[inline] - pub fn chmod(&self, new_mode: u16) -> Result<()> { - call::fchmod(self.raw(), new_mode) + pub fn ftruncate(self, len: usize) -> Result<()> { + call::ftruncate(self.raw(), len) } #[inline] - pub fn chown(&self, new_uid: u32, new_gid: u32) -> Result<()> { - call::fchown(self.raw(), new_uid, new_gid) + pub fn futimens(self, times: &[data::TimeSpec; 2]) -> Result<()> { + call::futimens(self.raw(), times) } - pub fn stat(&self) -> Result { - call::fstat(self.raw()) + /* TODO: Support unlinkat using std_fs_call + #[inline] + pub fn unlinkat(&self, path: &str, flags: i32) -> Result<()> { + call::unlinkat(self.raw(), path, flags) } - pub fn statvfs(&self) -> Result { - call::fstatvfs(self.raw()) + */ + #[inline] + pub fn fpath(&self, path: &mut [u8]) -> Result { + call::fpath(self.raw(), path) } - // TODO: futimens #[inline] pub fn close(self) -> Result<()> { call::close(self.into_raw()) @@ -477,14 +503,6 @@ pub mod call { Error::demux(unsafe { redox_write_v1(raw_fd, buf.as_ptr(), buf.len()) }) } #[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 fchmod(raw_fd: usize, new_mode: u16) -> Result<()> { Error::demux(unsafe { redox_fchmod_v1(raw_fd, new_mode) })?; Ok(()) @@ -495,8 +513,8 @@ pub mod call { Ok(()) } #[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()) }) + 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 { @@ -515,10 +533,34 @@ pub mod call { } } #[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 close(raw_fd: usize) -> Result<()> { Error::demux(unsafe { redox_close_v1(raw_fd) })?; @@ -741,3 +783,291 @@ pub mod call { .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, + } + #[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, + _ => 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, + }) + } + } +}