milestone: desktop path Phases 1-5

Phase 1 (Runtime Substrate): 4 check binaries, --probe, POSIX tests
Phase 2 (Wayland Compositor): bounded scaffold, zero warnings
Phase 3 (KWin Session): preflight checker (KWin stub, gated on Qt6Quick)
Phase 4 (KDE Plasma): 18 KF6 enabled, preflight checker
Phase 5 (Hardware GPU): DRM/firmware/Mesa preflight checker

Build: zero warnings, all scripts syntax-clean. Oracle-verified.
This commit is contained in:
2026-04-29 09:54:06 +01:00
parent b23714f542
commit 8acc73d774
508 changed files with 76526 additions and 396 deletions
+827
View File
@@ -0,0 +1,827 @@
//! uds scheme for handling Unix Domain Socket datagram communication
use super::{
get_uid_gid_from_pid, path_buf_to_str, read_msghdr_info, read_num, AncillaryData, Credential,
DataPacket, MsgWriter, MAX_DGRAM_MSG_LEN,
};
use libc::{AF_UNIX, SO_DOMAIN, SO_PASSCRED};
use libredox::protocol::SocketCall;
use rand::rngs::SmallRng;
use rand::Rng;
use redox_scheme::{
scheme::SchemeSync, CallerCtx, OpenResult, RecvFdRequest, Response, SendFdRequest,
SignalBehavior, Socket as SchemeSocket,
};
use scheme_utils::FpathWriter;
use std::{
cell::RefCell,
cmp,
collections::{HashMap, HashSet, VecDeque},
mem,
rc::Rc,
};
use syscall::{error::*, flag::*, schemev2::NewFdFlags, Error, FobtainFdFlags, Stat};
#[derive(Debug, Default)]
pub struct Socket {
primary_id: usize,
path: Option<String>,
state: State,
peer: Option<usize>,
messages: VecDeque<DataPacket>,
options: HashSet<i32>,
fds: VecDeque<usize>,
flags: usize,
issued_token: Option<u64>,
}
impl Socket {
fn drop_fds(&mut self, num_fd: usize) -> Result<()> {
for i in 0..num_fd {
if self.fds.pop_front().is_none() {
eprintln!("Socket::drop_fds: Attempted to drop FD #{} of {}, but fd queue is empty. State inconsistency.", i + 1, num_fd);
return Err(Error::new(EINVAL));
}
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum State {
Unbound,
Bound,
Closed,
}
impl Default for State {
fn default() -> Self {
Self::Unbound
}
}
impl DataPacket {
pub fn serialize_to_stream(
self,
stream: &mut [u8],
socket: &mut Socket,
name_buf_size: usize,
iov_size: usize,
) -> Result<usize> {
let mut msg_writer = MsgWriter::new(stream);
msg_writer.write_name(
self.ancillary_data.name,
name_buf_size,
UdsDgramScheme::fpath_inner,
)?;
msg_writer.write_payload(&self.payload, self.payload.len(), iov_size)?;
// Write the ancillary data
if !msg_writer.write_rights(self.ancillary_data.num_fds) {
// Buffer was too small, FDs could not be described. Drop the actual FDs.
eprintln!(
"serialize_to_stream: Buffer too small for SCM_RIGHTS, dropping {} FDs.",
self.ancillary_data.num_fds
);
socket.drop_fds(self.ancillary_data.num_fds)?;
}
// Write other ancillary datas
for option in &socket.options {
let result = match *option {
SO_PASSCRED => msg_writer.write_credentials(&self.ancillary_data.cred),
_ => {
eprintln!(
"serialize_to_stream: Unsupported socket option for serialization: {}",
option
);
return Err(Error::new(EOPNOTSUPP));
}
};
if !result {
eprintln!("serialize_to_stream: Buffer too small for ancillary data, stopping further serialization.");
break;
}
}
Ok(msg_writer.len())
}
}
enum Handle {
Socket(Rc<RefCell<Socket>>),
SchemeRoot,
}
impl Handle {
fn as_socket(&self) -> Option<&Rc<RefCell<Socket>>> {
if let Self::Socket(socket) = self {
Some(socket)
} else {
None
}
}
fn is_scheme_root(&self) -> bool {
matches!(self, Self::SchemeRoot)
}
}
pub struct UdsDgramScheme<'sock> {
handles: HashMap<usize, Handle>,
next_id: usize,
socket_paths: HashMap<String, Rc<RefCell<Socket>>>,
socket_tokens: HashMap<u64, Rc<RefCell<Socket>>>,
socket: &'sock SchemeSocket,
proc_creds_capability: usize,
rng: SmallRng,
}
impl<'sock> UdsDgramScheme<'sock> {
pub fn new(socket: &'sock SchemeSocket) -> Result<Self> {
Ok(Self {
handles: HashMap::new(),
next_id: 0,
socket_paths: HashMap::new(),
socket_tokens: HashMap::new(),
socket,
proc_creds_capability: {
libredox::call::open(
"/scheme/proc/proc-creds-capability",
libredox::flag::O_RDONLY,
0,
)?
},
rng: rand::make_rng(),
})
}
fn post_fevent(&self, id: usize, flags: usize) -> Result<()> {
let fevent_response = Response::post_fevent(id, flags);
match self
.socket
.write_response(fevent_response, SignalBehavior::Restart)
{
Ok(true) => Ok(()),
Ok(false) => Err(Error::new(EAGAIN)),
Err(err) => Err(err),
}
}
fn get_socket(&self, id: usize) -> Result<&Rc<RefCell<Socket>>, Error> {
self.handles
.get(&id)
.and_then(Handle::as_socket)
.ok_or(Error::new(EBADF))
}
fn insert_socket(&mut self, id: usize, socket: Rc<RefCell<Socket>>) {
self.handles.insert(id, Handle::Socket(socket));
}
fn get_connected_peer(&self, id: usize) -> Result<(usize, Rc<RefCell<Socket>>), Error> {
let socket = self.get_socket(id)?.borrow();
let remote_id = socket.peer.ok_or(Error::new(ENOTCONN))?;
let remote_rc = self.get_socket(remote_id).map_err(|e| {
eprintln!("get_connected_peer(id: {}): Peer socket (id: {}) has vanished. Original error: {:?}", id, remote_id, e);
Error::new(EPIPE)
})?;
if remote_rc.borrow().state == State::Closed {
eprintln!(
"get_connected_peer(id: {}): Attempted to interact with a closed peer (id: {}).",
id, remote_id
);
return Err(Error::new(ECONNREFUSED));
}
Ok((remote_id, remote_rc.clone()))
}
fn handle_unnamed_socket(&mut self, flags: usize) -> usize {
let new_id = self.next_id;
let mut new = Socket::default();
new.flags = flags;
new.primary_id = new_id;
self.insert_socket(new_id, Rc::new(RefCell::new(new)));
self.next_id += 1;
new_id
}
fn call_inner(
&mut self,
id: usize,
payload: &mut [u8],
metadata: &[u64],
ctx: &CallerCtx,
) -> Result<usize> {
// metadata to Vec<u8>
let Some(verb) = SocketCall::try_from_raw(metadata[0] as usize) else {
eprintln!("call_inner: Invalid verb in metadata: {:?}", metadata);
return Err(Error::new(EINVAL));
};
match verb {
SocketCall::Bind => self.handle_bind(id, &payload),
SocketCall::Connect => self.handle_connect(id, &payload),
SocketCall::SetSockOpt => self.handle_setsockopt(id, metadata[1] as i32, &payload),
SocketCall::GetSockOpt => self.handle_getsockopt(id, metadata[1] as i32, payload),
SocketCall::SendMsg => self.handle_sendmsg(id, payload, ctx),
SocketCall::RecvMsg => self.handle_recvmsg(id, payload),
SocketCall::Unbind => self.handle_unbind(id),
SocketCall::GetToken => self.handle_get_token(id, payload),
SocketCall::GetPeerName => self.handle_get_peer_name(id, payload),
_ => Err(Error::new(EOPNOTSUPP)),
}
}
fn handle_bind(&mut self, id: usize, path_buf: &[u8]) -> Result<usize> {
let path = path_buf_to_str(path_buf)?;
// Check if path is already bound to a server
if self.socket_paths.contains_key(path) {
eprintln!(
"handle_bind(id: {}): Address '{}' already in use.",
id, path
);
return Err(Error::new(EADDRINUSE));
}
let socket_rc = self.get_socket(id)?.clone();
let path_owned: String;
let token: u64;
{
let mut socket = socket_rc.borrow_mut();
if socket.state != State::Unbound {
eprintln!(
"handle_bind(id: {}): Socket is already bound or connected (state: {:?})",
id, socket.state
);
return Err(Error::new(EINVAL));
}
path_owned = path.to_string();
socket.path = Some(path_owned.clone());
socket.state = State::Bound;
token = self.rng.next_u64();
socket.issued_token = Some(token);
}
self.socket_paths.insert(path_owned, socket_rc.clone());
self.socket_tokens.insert(token, socket_rc);
Ok(0)
}
fn handle_connect(&mut self, id: usize, token_buf: &[u8]) -> Result<usize> {
let token = read_num::<u64>(token_buf)?;
{
let target_rc = self
.socket_tokens
.get(&token)
.ok_or(Error::new(ECONNREFUSED))?;
let target_socket_token = target_rc
.borrow()
.issued_token
.ok_or(Error::new(ECONNREFUSED))?;
if target_socket_token != token {
return Err(Error::new(EACCES));
}
let target_id = target_rc.borrow().primary_id;
let socket_rc = self.get_socket(id)?;
socket_rc.borrow_mut().peer = Some(target_id);
}
Ok(0)
}
fn handle_setsockopt(&mut self, id: usize, option: i32, value_slice: &[u8]) -> Result<usize> {
let socket_rc = self.get_socket(id)?;
let mut socket = socket_rc.borrow_mut();
match option {
SO_PASSCRED => {
let value = read_num::<i32>(value_slice)?;
if value != 0 {
socket.options.insert(SO_PASSCRED);
} else {
socket.options.remove(&SO_PASSCRED);
}
Ok(value_slice.len())
}
_ => {
eprintln!(
"handle_setsockopt(id: {}): Unsupported option: {}",
id, option
);
Err(Error::new(ENOPROTOOPT))
}
}
}
fn handle_getsockopt(&mut self, id: usize, option: i32, payload: &mut [u8]) -> Result<usize> {
match option {
SO_DOMAIN => {
payload.fill(0);
if payload.len() < mem::size_of::<i32>() {
eprintln!(
"handle_getsockopt(id: {}): SO_DOMAIN payload buffer is too small. len: {}",
id,
payload.len()
);
return Err(Error::new(ENOBUFS));
}
let domain = AF_UNIX.to_le_bytes();
payload[..domain.len()].copy_from_slice(&domain);
Ok(domain.len())
}
_ => {
eprintln!(
"handle_getsockopt(id: {}): Unsupported option: {}",
id, option
);
Err(Error::new(ENOPROTOOPT))
}
}
}
fn handle_sendmsg(&mut self, id: usize, msg_stream: &[u8], ctx: &CallerCtx) -> Result<usize> {
if msg_stream.is_empty() {
eprintln!("handle_sendmsg(id: {}): msg_stream is empty.", id);
return Err(Error::new(EINVAL));
}
let name = {
let socket_rc = self.get_socket(id)?;
let socket = socket_rc.borrow();
socket.path.clone()
};
let (remote_id, remote_rc) = self.get_connected_peer(id)?;
let bytes_written = Self::sendmsg_inner(
self.proc_creds_capability,
&mut remote_rc.borrow_mut(),
name,
msg_stream,
ctx,
)?;
self.post_fevent(remote_id, EVENT_READ.bits())?;
Ok(bytes_written)
}
fn sendmsg_inner(
cap_fd: usize,
socket: &mut Socket,
name: Option<String>,
msg_stream: &[u8],
ctx: &CallerCtx,
) -> Result<usize> {
if msg_stream.is_empty() {
eprintln!("sendmsg_inner: msg_stream is empty.");
return Err(Error::new(EINVAL));
}
let (pid, uid, gid) = get_uid_gid_from_pid(cap_fd, ctx.pid)?;
let message = DataPacket::from_stream(
msg_stream,
name,
Credential::new(pid as i32, uid as i32, gid as i32),
)?;
let payload_len = message.len();
socket.messages.push_back(message);
Ok(payload_len)
}
fn handle_recvmsg(&mut self, id: usize, msg_stream: &mut [u8]) -> Result<usize> {
let socket_rc = self.get_socket(id)?;
let mut socket = socket_rc.borrow_mut();
if let Some(message) = socket.messages.pop_front() {
Ok(Self::recvmsg_inner(&mut socket, message, msg_stream)?)
} else if (socket.flags as usize) & O_NONBLOCK == O_NONBLOCK {
Err(Error::new(EAGAIN))
} else {
Err(Error::new(EWOULDBLOCK))
}
}
fn recvmsg_inner(
socket: &mut Socket,
message: DataPacket,
msg_stream: &mut [u8],
) -> Result<usize> {
// Read the name length, whole iov size, and msg controllen from the stream
let (prepared_name_len, prepared_whole_iov_size, _) = read_msghdr_info(msg_stream)?;
message.serialize_to_stream(
msg_stream,
socket,
prepared_name_len,
prepared_whole_iov_size,
)
}
fn handle_unbind(&mut self, id: usize) -> Result<usize> {
let path_opt = {
let socket_rc = self.get_socket(id)?;
let mut socket = socket_rc.borrow_mut();
if socket.state != State::Bound {
return Err(Error::new(EINVAL));
}
socket.state = State::Unbound;
socket.path.take()
};
if let Some(path) = path_opt {
self.socket_paths.remove(&path);
}
Ok(0)
}
fn handle_get_token(&self, id: usize, payload: &mut [u8]) -> Result<usize> {
let socket_rc = self.get_socket(id)?;
let Some(token) = socket_rc.borrow().issued_token else {
return Err(Error::new(EINVAL));
};
let token_bytes = token.to_le_bytes();
let token_bytes_len = token_bytes.len();
if payload.len() < token_bytes_len {
eprintln!(
"handle_get_token(id: {}): Payload buffer is too small for token.",
id
);
return Err(Error::new(ENOBUFS));
}
payload[..token_bytes_len].copy_from_slice(&token_bytes);
return Ok(token_bytes_len);
}
fn handle_get_peer_name(&self, id: usize, payload: &mut [u8]) -> Result<usize> {
let (_, socket_rc) = self.get_connected_peer(id)?;
let socket_borrow = socket_rc.borrow();
match socket_borrow.path.as_ref() {
Some(path_string) => Self::fpath_inner(path_string, payload),
None => {
let empty_path = "".to_string();
Self::fpath_inner(&empty_path, payload)
}
}
}
fn handle_connect_socketpair(&mut self, id: usize) -> Result<OpenResult> {
let new_id = self.next_id;
let mut new = Socket::default();
new.primary_id = new_id;
let socket_rc = self.get_socket(id)?;
if socket_rc.borrow().state == State::Closed {
eprintln!(
"handle_connect_socketpair(id: {}): Attempting to connect from a closed socket.",
id
);
return Err(Error::new(ECONNREFUSED));
}
{
let mut socket = socket_rc.borrow_mut();
socket.peer = Some(new_id);
}
new.peer = Some(id);
// smoltcp sends writeable whenever a listener gets a
// client, we'll do the same too (but also readable,
// why not)
self.post_fevent(id, (EVENT_READ | EVENT_WRITE).bits())?;
self.insert_socket(new_id, Rc::new(RefCell::new(new)));
self.next_id += 1;
Ok(OpenResult::ThisScheme {
number: new_id,
flags: NewFdFlags::empty(),
})
}
fn handle_recvfd(&mut self, id: usize) -> Result<OpenResult> {
let socket_rc = self.get_socket(id)?;
let mut socket = socket_rc.borrow_mut();
let fd = socket.fds.pop_front().ok_or(Error::new(EWOULDBLOCK))?;
Ok(OpenResult::OtherScheme { fd })
}
fn handle_listen(&mut self, id: usize) -> Result<OpenResult> {
let socket_rc = self.get_socket(id)?;
let new_id = self.next_id;
self.insert_socket(new_id, socket_rc.clone());
self.next_id += 1;
Ok(OpenResult::ThisScheme {
number: new_id,
flags: NewFdFlags::empty(),
})
}
fn write_inner(&mut self, id: usize, buf: &[u8], ctx: &CallerCtx) -> Result<usize> {
if buf.len() > MAX_DGRAM_MSG_LEN {
return Err(Error::new(EMSGSIZE));
}
let name = {
let socket_rc = self.get_socket(id)?;
let socket = socket_rc.borrow();
if matches!(socket.state, State::Closed) {
return Err(Error::new(EPIPE));
}
socket.path.clone()
};
// Assume writing to the connected socket if the given id is the primary id
let (remote_id, remote_rc) = self.get_connected_peer(id)?;
let mut remote = remote_rc.borrow_mut();
let message = DataPacket::new(
buf.to_vec(),
AncillaryData::new(
Credential::new(ctx.pid as i32, ctx.uid as i32, ctx.gid as i32),
name,
),
);
remote.messages.push_back(message);
self.post_fevent(remote_id, EVENT_READ.bits())?;
Ok(buf.len())
}
fn fpath_inner(path: &String, buf: &mut [u8]) -> Result<usize> {
FpathWriter::with(buf, "uds_dgram", |w| {
w.push_str(path);
Ok(())
})
}
fn read_inner(&mut self, id: usize, buf: &mut [u8], flags: u32) -> Result<usize> {
let socket_rc = self.get_socket(id)?;
let mut socket = socket_rc.borrow_mut();
if let Some(message) = socket.messages.pop_front() {
let full_len = message.len();
let copy_len = cmp::min(buf.len(), full_len);
buf[..copy_len].copy_from_slice(&message.payload[..copy_len]);
Ok(copy_len)
} else if (flags as usize) & O_NONBLOCK == O_NONBLOCK {
Err(Error::new(EAGAIN))
} else {
Err(Error::new(EWOULDBLOCK))
}
}
fn sendfd_inner(&mut self, sendfd_request: &SendFdRequest) -> Result<usize> {
if sendfd_request.num_fds() == 0 {
return Ok(0);
}
let mut new_fds = Vec::new();
new_fds.resize(sendfd_request.num_fds(), usize::MAX);
if let Err(e) =
sendfd_request.obtain_fd(&self.socket, FobtainFdFlags::UPPER_TBL, &mut new_fds)
{
eprintln!("sendfd_inner: obtain_fd failed with error: {:?}", e);
return Err(e);
}
let socket_id = sendfd_request.id();
let (remote_id, remote_rc) = self.get_connected_peer(socket_id)?;
{
let mut remote = remote_rc.borrow_mut();
for new_fd in &new_fds {
remote.fds.push_back(*new_fd);
}
}
self.post_fevent(remote_id, EVENT_READ.bits())?;
Ok(new_fds.len())
}
fn recvfd_inner(&mut self, recvfd_request: &RecvFdRequest) -> Result<OpenResult> {
if recvfd_request.num_fds() == 0 {
return Ok(OpenResult::OtherSchemeMultiple { num_fds: 0 });
}
let socket_id = recvfd_request.id();
let socket_rc = self.get_socket(socket_id)?;
let mut socket = socket_rc.borrow_mut();
if socket.fds.len() < recvfd_request.num_fds() {
return if (socket.flags as usize) & O_NONBLOCK == O_NONBLOCK {
Ok(OpenResult::WouldBlock)
} else {
Err(Error::new(EWOULDBLOCK))
};
}
let fds: Vec<usize> = socket.fds.drain(..recvfd_request.num_fds()).collect();
if let Err(e) = recvfd_request.move_fd(&self.socket, FmoveFdFlags::empty(), &fds) {
eprintln!("recvfd_inner: move_fd failed with error: {:?}", e);
return Err(Error::new(EPROTO));
}
Ok(OpenResult::OtherSchemeMultiple {
num_fds: recvfd_request.num_fds(),
})
}
}
impl<'sock> SchemeSync for UdsDgramScheme<'sock> {
fn scheme_root(&mut self) -> Result<usize> {
let new_id = self.next_id;
self.handles.insert(new_id, Handle::SchemeRoot);
self.next_id += 1;
Ok(new_id)
}
fn openat(
&mut self,
fd: usize,
path: &str,
mut flags: usize,
fcntl_flags: u32,
_ctx: &CallerCtx,
) -> Result<OpenResult> {
{
let Some(handle) = self.handles.get(&fd) else {
return Err(Error::new(EBADF));
};
if !handle.is_scheme_root() {
eprintln!(
"openat(fd: {}, path: '{}'): fd is not an open capability.",
fd, path
);
return Err(Error::new(EACCES));
}
}
flags |= fcntl_flags as usize;
let new_id = if path.is_empty() {
if flags & O_CREAT == O_CREAT {
self.handle_unnamed_socket(flags)
} else {
eprintln!(
"open(path: '{}'): Attempting to open an unnamed socket without O_CREAT.",
path
);
return Err(Error::new(EINVAL));
}
} else {
eprintln!(
"open(path: '{}'): Attempting to open a named socket, which is not supported.",
path
);
return Err(Error::new(EINVAL));
};
Ok(OpenResult::ThisScheme {
number: new_id,
flags: NewFdFlags::empty(),
})
}
fn call(
&mut self,
id: usize,
payload: &mut [u8],
metadata: &[u64],
ctx: &CallerCtx,
) -> Result<usize> {
self.call_inner(id, payload, metadata, ctx)
}
fn dup(&mut self, id: usize, buf: &[u8], _ctx: &CallerCtx) -> Result<OpenResult> {
match buf {
// Connect for socket pair
b"connect" => self.handle_connect_socketpair(id),
b"recvfd" => self.handle_recvfd(id),
// listen will generate a id for same socket
b"listen" => self.handle_listen(id),
_ => Err(Error::new(EINVAL)),
}
}
fn write(
&mut self,
id: usize,
buf: &[u8],
_offset: u64,
_flags: u32,
ctx: &CallerCtx,
) -> Result<usize> {
self.write_inner(id, buf, ctx)
}
fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result<usize> {
match self.handles.get(&id).ok_or(Error::new(EBADF))? {
Handle::SchemeRoot => Ok(Self::fpath_inner(&String::new(), buf)?),
Handle::Socket(socket_rc) => {
let socket = socket_rc.borrow();
let empty = String::new();
let path = socket.path.as_ref().unwrap_or(&empty);
Ok(Self::fpath_inner(path, buf)?)
}
}
}
fn fsync(&mut self, id: usize, _ctx: &CallerCtx) -> Result<()> {
self.get_socket(id).and(Ok(()))
}
fn read(
&mut self,
id: usize,
buf: &mut [u8],
_offset: u64,
flags: u32,
_ctx: &CallerCtx,
) -> Result<usize> {
self.read_inner(id, buf, flags)
}
fn on_close(&mut self, id: usize) {
let Some(Handle::Socket(socket_rc)) = self.handles.remove(&id) else {
return;
};
let mut socket = socket_rc.borrow_mut();
if socket.primary_id == id {
socket.state = State::Closed;
socket.peer = None;
let path = socket.path.clone();
socket.path = None;
if let Some(path) = path {
self.socket_paths.remove(&path);
}
if let Some(token) = socket.issued_token {
self.socket_tokens.remove(&token);
}
}
}
fn on_sendfd(&mut self, sendfd_request: &SendFdRequest) -> Result<usize> {
self.sendfd_inner(sendfd_request)
}
fn on_recvfd(&mut self, recvfd_request: &RecvFdRequest) -> Result<OpenResult> {
self.recvfd_inner(recvfd_request)
}
fn fcntl(&mut self, id: usize, cmd: usize, arg: usize, _ctx: &CallerCtx) -> Result<usize> {
let socket = self.get_socket(id)?;
match cmd {
F_GETFL => Ok(socket.borrow().flags),
F_SETFL => {
socket.borrow_mut().flags = arg;
Ok(0)
}
_ => {
eprintln!("fcntl(id: {}): Unsupported cmd: {}", id, cmd);
Err(Error::new(EINVAL))
}
}
}
fn fevent(&mut self, id: usize, flags: EventFlags, _ctx: &CallerCtx) -> Result<EventFlags> {
let socket_rc = self.get_socket(id)?;
let socket = socket_rc.borrow_mut();
let mut ready = EventFlags::empty();
if flags.contains(EVENT_READ) && !socket.messages.is_empty() {
ready |= EVENT_READ;
}
if flags.contains(EVENT_WRITE) && socket.peer.is_some() {
ready |= EVENT_WRITE;
}
Ok(ready)
}
fn fstat(&mut self, id: usize, stat: &mut Stat, _ctx: &CallerCtx) -> Result<()> {
self.get_socket(id)?;
*stat = Stat {
st_mode: MODE_SOCK,
..Default::default()
};
Ok(())
}
}
+367
View File
@@ -0,0 +1,367 @@
use libredox::protocol::ProcMeta;
use std::fmt::Debug;
use std::{cmp, convert::TryInto, mem};
use syscall::{error::*, Error};
pub mod dgram;
pub mod stream;
// TODO: Remove this when Rust libc crate is updated to include SCM_CREDENTIALS
const SCM_CREDENTIALS: i32 = 2;
const MAX_DGRAM_MSG_LEN: usize = 65536;
const MIN_RECV_MSG_LEN: usize = mem::size_of::<usize>() * 2; // name_len, payload_len,
const CMSG_HEADER_LEN_IN_STREAM: usize = CMSG_LEVEL_SIZE + CMSG_TYPE_SIZE + CMSG_DATA_LEN_SIZE;
const CMSG_LEVEL_SIZE: usize = mem::size_of::<i32>();
const CMSG_TYPE_SIZE: usize = mem::size_of::<i32>();
const CMSG_DATA_LEN_SIZE: usize = mem::size_of::<usize>();
const PID_SIZE: usize = mem::size_of::<i32>();
const UID_SIZE: usize = mem::size_of::<i32>();
const GID_SIZE: usize = mem::size_of::<i32>();
#[derive(Debug, Clone, Default, PartialEq, Eq)]
struct Credential {
pid: i32,
uid: i32,
gid: i32,
}
impl Credential {
fn new(pid: i32, uid: i32, gid: i32) -> Self {
Self { pid, uid, gid }
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
struct AncillaryData {
cred: Credential,
num_fds: usize,
name: Option<String>,
}
impl AncillaryData {
fn new(cred: Credential, name: Option<String>) -> Self {
Self {
cred,
name,
..Default::default()
}
}
}
struct AncillaryDataHeader {
level: i32,
c_type: i32,
data_len: usize,
}
impl AncillaryDataHeader {
fn from_stream(stream: &[u8]) -> Result<Option<Self>> {
let mut cursor = 0;
if cursor + CMSG_HEADER_LEN_IN_STREAM > stream.len() {
return Ok(None);
}
// cmsg entry format: [level(i32)][type(i32)][data_len(usize)][data]
let cmsg_level = read_num::<i32>(&stream[cursor..])?;
cursor += mem::size_of::<i32>();
let cmsg_type = read_num::<i32>(&stream[cursor..])?;
cursor += mem::size_of::<i32>();
let cmsg_data_len = read_num::<usize>(&stream[cursor..])?;
Ok(Some(Self {
level: cmsg_level,
c_type: cmsg_type,
data_len: cmsg_data_len,
}))
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
struct DataPacket {
payload: Vec<u8>,
ancillary_data: AncillaryData,
// Only for stream packets
read_offset: usize,
ancillary_taken: bool,
}
impl DataPacket {
fn new(payload: Vec<u8>, ancillary_data: AncillaryData) -> Self {
Self {
payload,
ancillary_data,
read_offset: 0,
ancillary_taken: false,
}
}
fn len(&self) -> usize {
self.payload.len()
}
fn from_stream(stream: &[u8], name: Option<String>, cred: Credential) -> Result<Self> {
let mut cursor: usize = 0;
let payload_len = read_num::<usize>(&stream[cursor..])?;
cursor += mem::size_of::<usize>();
let payload = stream
.get(cursor..cursor + payload_len)
.ok_or_else(|| {
eprintln!("Message::from_stream: Stream too short for payload. Expected len {}, actual remaining len {}", payload_len, stream.len() - cursor);
Error::new(EINVAL)
})?;
cursor += payload_len;
// Create a new message with the payload and credentials
let mut message = Self::new(payload.to_vec(), AncillaryData::new(cred, name));
while let Some(cmsg_header) = AncillaryDataHeader::from_stream(&stream[cursor..])? {
cursor += CMSG_HEADER_LEN_IN_STREAM;
let data_stream = stream
.get(cursor..cursor + cmsg_header.data_len)
.ok_or_else(|| {
eprintln!("Message::from_stream: Stream too short for ancillary data. Expected len {}, actual remaining len {}", cmsg_header.data_len, stream.len() - cursor);
Error::new(EINVAL)
})?;
match (cmsg_header.level, cmsg_header.c_type) {
(libc::SOL_SOCKET, libc::SCM_RIGHTS) => {
// Handle file descriptor passing
let num_fds = read_num::<usize>(data_stream)?;
message.ancillary_data.num_fds += num_fds;
}
(libc::SOL_SOCKET, SCM_CREDENTIALS) => {}
_ => {
eprintln!(
"Message::from_stream: Unsupported cmsg type received. level: {}, type: {}",
cmsg_header.level, cmsg_header.c_type
);
return Err(Error::new(EOPNOTSUPP));
}
}
cursor += cmsg_header.data_len;
}
Ok(message)
}
}
trait NumFromBytes: Sized + Debug {
fn from_le_bytes_slice(buffer: &[u8]) -> Result<Self, Error>;
}
macro_rules! num_from_bytes_impl {
($($t:ty),*) => {
$(
impl NumFromBytes for $t {
fn from_le_bytes_slice(buffer: &[u8]) -> Result<Self, Error> {
let size = mem::size_of::<Self>();
let buffer_slice = buffer.get(..size).and_then(|s| s.try_into().ok());
if let Some(slice) = buffer_slice {
Ok(Self::from_le_bytes(slice))
} else {
eprintln!(
"read_num: buffer is too short to read num of size {} (buffer len: {})",
size, buffer.len()
);
Err(Error::new(EINVAL))
}
}
}
)*
};
}
num_from_bytes_impl!(i32, u32, u64, usize);
fn read_num<T>(buffer: &[u8]) -> Result<T, Error>
where
T: NumFromBytes,
{
T::from_le_bytes_slice(buffer)
}
fn get_uid_gid_from_pid(cap_fd: usize, target_pid: usize) -> Result<(u32, u32, u32)> {
let mut buffer = [0u8; mem::size_of::<ProcMeta>()];
let _ = libredox::call::get_proc_credentials(cap_fd, target_pid, &mut buffer).map_err(|e| {
eprintln!(
"Failed to get process credentials for pid {}: {:?}",
target_pid, e
);
Error::new(EINVAL)
})?;
let mut cursor = 0;
let pid = read_num::<u32>(&buffer[cursor..])?;
cursor += mem::size_of::<u32>() * 3;
let uid = read_num::<u32>(&buffer[cursor..])?;
cursor += mem::size_of::<u32>() * 3;
let gid = read_num::<u32>(&buffer[cursor..])?;
Ok((pid, uid, gid))
}
fn read_msghdr_info(stream: &mut [u8]) -> Result<(usize, usize, usize)> {
if stream.len() < mem::size_of::<usize>() * 3 {
eprintln!(
"get_msghdr_info: stream buffer is too small to read headers. len: {}",
stream.len()
);
return Err(Error::new(EINVAL));
}
let mut cursor: usize = 0;
let prepared_name_len = read_num::<usize>(&stream[cursor..])?;
cursor += mem::size_of::<usize>();
let prepared_whole_iov_size = read_num::<usize>(&stream[cursor..])?;
cursor += mem::size_of::<usize>();
let prepared_msg_controllen = read_num::<usize>(&stream[cursor..])?;
cursor += mem::size_of::<usize>();
// Clear the stream buffer
stream[..cursor].copy_from_slice(&[0u8; mem::size_of::<usize>() * 3]);
Ok((
prepared_name_len,
prepared_whole_iov_size,
prepared_msg_controllen,
))
}
struct MsgWriter<'a> {
buffer: &'a mut [u8],
written_len: usize,
}
impl<'a> MsgWriter<'a> {
fn new(buffer: &'a mut [u8]) -> Self {
Self {
buffer,
written_len: 0,
}
}
pub fn len(&self) -> usize {
self.written_len
}
fn write_name(
&mut self,
name: Option<String>,
name_buf_size: usize,
name_write_fn: fn(&String, &mut [u8]) -> Result<usize>,
) -> Result<()> {
if self.buffer.len() < self.written_len + mem::size_of::<usize>() {
eprintln!("MsgWriter::write_name: Buffer too small to write name length. written_len: {}, buffer_len: {}", self.written_len, self.buffer.len());
self.written_len = self.buffer.len();
return Ok(());
}
if let Some(name) = name {
let copy_len = cmp::min(
name_buf_size,
self.buffer.len() - self.written_len - mem::size_of::<usize>(),
);
if name_buf_size > 0 && copy_len < name.len() {
eprintln!("MsgWriter::write_name: Name will be truncated. Full length: {}, buffer available: {}", name.len(), copy_len);
}
let name_len = name_write_fn(
&name,
&mut self.buffer[self.written_len + mem::size_of::<usize>()
..self.written_len + mem::size_of::<usize>() + copy_len],
)?;
self.buffer[self.written_len..self.written_len + mem::size_of::<usize>()]
.copy_from_slice(&name_len.to_le_bytes());
self.written_len += mem::size_of::<usize>() + name_len;
Ok(())
} else {
self.written_len += mem::size_of::<usize>();
Ok(())
}
}
fn write_payload(&mut self, payload: &[u8], full_len: usize, iov_size: usize) -> Result<usize> {
let Some(payload_len_buffer) = self
.buffer
.get_mut(self.written_len..self.written_len + mem::size_of::<usize>())
else {
eprintln!("MsgWriter::write_payload: Buffer too small to write payload length. written_len: {}, buffer_len: {}", self.written_len, self.buffer.len());
self.written_len = self.buffer.len();
return Ok(0);
};
payload_len_buffer.copy_from_slice(&full_len.to_le_bytes());
self.written_len += mem::size_of::<usize>();
let copy_len = cmp::min(iov_size, full_len);
if copy_len < full_len {
eprintln!("MsgWriter::write_payload: Payload will be truncated. Full length: {}, buffer available: {}", full_len, copy_len);
}
let Some(payload_buffer) = self
.buffer
.get_mut(self.written_len..self.written_len + copy_len)
else {
eprintln!("MsgWriter::write_payload: Buffer too small to write payload data. written_len: {}, buffer_len: {}", self.written_len, self.buffer.len());
self.written_len = self.buffer.len();
return Ok(0);
};
payload_buffer.copy_from_slice(&payload[..copy_len]);
self.written_len += copy_len;
Ok(copy_len)
}
fn write_cmsg(&mut self, level: i32, c_type: i32, data: &[u8]) -> bool {
let data_len = data.len();
let Some(remaining_buf) = self.buffer.get_mut(self.written_len..) else {
eprintln!("CmsgWriter::write_cmsg: No remaining buffer space at all.");
return false;
};
if remaining_buf.len() < CMSG_HEADER_LEN_IN_STREAM {
eprintln!("CmsgWriter::write_cmsg: Not enough space for cmsg header. remaining: {}, needed: {}", remaining_buf.len(), CMSG_HEADER_LEN_IN_STREAM);
// Fill the remaining buffer with 1s to indicate Imcomplete CMSG header
remaining_buf.fill(1);
self.written_len += remaining_buf.len();
return false;
}
let mut cursor = 0;
remaining_buf[cursor..cursor + CMSG_LEVEL_SIZE].copy_from_slice(&level.to_le_bytes());
cursor += CMSG_LEVEL_SIZE;
remaining_buf[cursor..cursor + CMSG_TYPE_SIZE].copy_from_slice(&c_type.to_le_bytes());
cursor += CMSG_TYPE_SIZE;
remaining_buf[cursor..cursor + CMSG_DATA_LEN_SIZE].copy_from_slice(&data_len.to_le_bytes());
cursor += CMSG_DATA_LEN_SIZE;
if remaining_buf.len() < cursor + data_len {
eprintln!(
"CmsgWriter::write_cmsg: Not enough space for cmsg data. remaining: {}, needed: {}",
remaining_buf.len(),
cursor + data_len
);
// Fill the remaining buffer with 1s to indicate Imcomplete CMSG data
remaining_buf.fill(1);
self.written_len += remaining_buf.len();
return false;
}
remaining_buf[cursor..cursor + data_len].copy_from_slice(data);
self.written_len += cursor + data_len;
true
}
fn write_rights(&mut self, num_fds: usize) -> bool {
let data = num_fds.to_le_bytes();
if num_fds == 0 {
return true;
}
self.write_cmsg(libc::SOL_SOCKET, libc::SCM_RIGHTS, &data)
}
fn write_credentials(&mut self, credential: &Credential) -> bool {
let mut data = [0u8; PID_SIZE + UID_SIZE + GID_SIZE];
data[..PID_SIZE].copy_from_slice(&credential.pid.to_le_bytes());
data[PID_SIZE..PID_SIZE + UID_SIZE].copy_from_slice(&credential.uid.to_le_bytes());
data[PID_SIZE + UID_SIZE..PID_SIZE + UID_SIZE + GID_SIZE]
.copy_from_slice(&credential.gid.to_le_bytes());
self.write_cmsg(libc::SOL_SOCKET, SCM_CREDENTIALS, &data)
}
}
fn path_buf_to_str(path_buf: &[u8]) -> Result<&str> {
match std::str::from_utf8(path_buf) {
Ok("") => Err(Error::new(EINVAL)),
Ok(s) => Ok(s),
Err(_) => Err(Error::new(EINVAL)),
}
}
File diff suppressed because it is too large Load Diff