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:
@@ -0,0 +1,446 @@
|
||||
use redox_scheme::{scheme::SchemeSync, CallerCtx, OpenResult, Response, SignalBehavior, Socket};
|
||||
use scheme_utils::FpathWriter;
|
||||
use std::{
|
||||
cmp,
|
||||
collections::{HashMap, VecDeque},
|
||||
};
|
||||
use syscall::{error::*, flag::*, schemev2::NewFdFlags, Error};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Client {
|
||||
buffer: Vec<u8>,
|
||||
remote: Connection,
|
||||
}
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Listener {
|
||||
path: Option<String>,
|
||||
awaiting: VecDeque<usize>,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub enum Extra {
|
||||
Client(Client),
|
||||
Listener(Listener),
|
||||
SchemeRoot,
|
||||
}
|
||||
impl Default for Extra {
|
||||
fn default() -> Self {
|
||||
Extra::Client(Client::default())
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Connection {
|
||||
Waiting,
|
||||
Open(usize),
|
||||
Closed,
|
||||
}
|
||||
impl Default for Connection {
|
||||
fn default() -> Self {
|
||||
Connection::Waiting
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Handle {
|
||||
flags: usize,
|
||||
extra: Extra,
|
||||
path: Option<String>,
|
||||
}
|
||||
impl Handle {
|
||||
/// Duplicate this listener handle into one that is linked to the
|
||||
/// specified remote.
|
||||
/// Does NOT error if this is not a listener
|
||||
pub fn accept(&self, remote: usize) -> Self {
|
||||
Self {
|
||||
flags: self.flags,
|
||||
extra: Extra::Client(Client {
|
||||
remote: Connection::Open(remote),
|
||||
..Client::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark this listener handle as having a connection which can be
|
||||
/// accepted, but only if it is ready to accept.
|
||||
/// Errors if this is not a listener
|
||||
pub fn connect(&mut self, other: usize) -> Result<()> {
|
||||
match self.extra {
|
||||
Extra::Listener(ref mut listener) => {
|
||||
listener.awaiting.push_back(other);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(Error::new(EBADF)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Error if this is not a listener
|
||||
pub fn require_listener(&mut self) -> Result<&mut Listener> {
|
||||
match self.extra {
|
||||
Extra::Listener(ref mut listener) => Ok(listener),
|
||||
_ => Err(Error::new(EBADF)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Error if this is not a client
|
||||
pub fn require_client(&mut self) -> Result<&mut Client> {
|
||||
match self.extra {
|
||||
Extra::Client(ref mut client) => Ok(client),
|
||||
_ => Err(Error::new(EBADF)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ChanScheme<'sock> {
|
||||
handles: HashMap<usize, Handle>,
|
||||
listeners: HashMap<String, usize>,
|
||||
next_id: usize,
|
||||
socket: &'sock Socket,
|
||||
}
|
||||
impl<'sock> ChanScheme<'sock> {
|
||||
pub fn new(socket: &'sock Socket) -> Self {
|
||||
Self {
|
||||
handles: HashMap::new(),
|
||||
listeners: HashMap::new(),
|
||||
next_id: 0,
|
||||
socket,
|
||||
}
|
||||
}
|
||||
|
||||
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(()), // Write response success
|
||||
Ok(false) => Err(Error::new(EAGAIN)), // Write response failed, retry.
|
||||
Err(err) => Err(err), // Error writing response
|
||||
}
|
||||
}
|
||||
|
||||
fn open(&mut self, path: &str, flags: usize) -> Result<OpenResult> {
|
||||
let new_id = self.next_id;
|
||||
let mut new = Handle::default();
|
||||
new.flags = flags;
|
||||
|
||||
let create = flags & O_CREAT == O_CREAT;
|
||||
|
||||
if create && !self.listeners.contains_key(path) {
|
||||
let mut listener = Listener::default();
|
||||
if !path.is_empty() {
|
||||
self.listeners.insert(String::from(path), new_id);
|
||||
listener.path = Some(String::from(path));
|
||||
}
|
||||
new.extra = Extra::Listener(listener);
|
||||
} else if create && flags & O_EXCL == O_EXCL {
|
||||
return Err(Error::new(EEXIST));
|
||||
} else {
|
||||
// Connect to existing if: O_CREAT isn't set or it already exists
|
||||
// and O_EXCL isn't set
|
||||
let listener_id = *self.listeners.get(path).ok_or(Error::new(ENOENT))?;
|
||||
let listener = self
|
||||
.handles
|
||||
.get_mut(&listener_id)
|
||||
.expect("orphan listener left over");
|
||||
listener.connect(new_id)?;
|
||||
|
||||
// smoltcp sends writeable whenever a listener gets a
|
||||
// client, we'll do the same too (but also readable, why
|
||||
// not)
|
||||
self.post_fevent(listener_id, (EVENT_READ | EVENT_WRITE).bits())?;
|
||||
}
|
||||
|
||||
self.handles.insert(new_id, new);
|
||||
self.next_id += 1;
|
||||
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: new_id,
|
||||
flags: NewFdFlags::empty(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'sock> SchemeSync for ChanScheme<'sock> {
|
||||
fn scheme_root(&mut self) -> Result<usize> {
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
|
||||
self.handles.insert(
|
||||
id,
|
||||
Handle {
|
||||
flags: 0,
|
||||
extra: Extra::SchemeRoot,
|
||||
path: None,
|
||||
},
|
||||
);
|
||||
Ok(id)
|
||||
}
|
||||
// ___ ____ _____ _ _
|
||||
// / _ \| _ \| ____| \ | |
|
||||
// | | | | |_) | _| | \| |
|
||||
// | |_| | __/| |___| |\ |
|
||||
// \___/|_| |_____|_| \_|
|
||||
fn openat(
|
||||
&mut self,
|
||||
dirfd: usize,
|
||||
path: &str,
|
||||
flags: usize,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<OpenResult> {
|
||||
let handle = self.handles.get(&dirfd).ok_or(Error::new(EBADF))?;
|
||||
|
||||
if !matches!(handle.extra, Extra::SchemeRoot) {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
|
||||
self.open(path, flags)
|
||||
}
|
||||
fn dup(&mut self, id: usize, buf: &[u8], _ctx: &CallerCtx) -> Result<OpenResult> {
|
||||
match buf {
|
||||
b"listen" => {
|
||||
loop {
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
let listener = handle.require_listener()?;
|
||||
let listener_path = listener.path.clone();
|
||||
|
||||
break if let Some(remote_id) = listener.awaiting.pop_front() {
|
||||
let new_id = self.next_id;
|
||||
let mut new = handle.accept(remote_id);
|
||||
|
||||
// Hook the remote side, assuming it's still
|
||||
// connected, up to this one so the connection is
|
||||
// mutal.
|
||||
let remote = match self.handles.get_mut(&remote_id) {
|
||||
Some(client) => client,
|
||||
None => continue, // Check next client
|
||||
};
|
||||
match remote.extra {
|
||||
Extra::Client(ref mut client) => {
|
||||
client.remote = Connection::Open(new_id);
|
||||
}
|
||||
Extra::Listener(_) => {
|
||||
panic!("newly created handle can't possibly be a listener")
|
||||
}
|
||||
Extra::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
}
|
||||
self.post_fevent(remote_id, EVENT_WRITE.bits())?;
|
||||
|
||||
new.path = listener_path;
|
||||
|
||||
self.handles.insert(new_id, new);
|
||||
self.next_id += 1;
|
||||
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: new_id,
|
||||
flags: NewFdFlags::empty(),
|
||||
})
|
||||
} else if handle.flags & O_NONBLOCK == O_NONBLOCK {
|
||||
Ok(OpenResult::WouldBlock)
|
||||
} else {
|
||||
Err(Error::new(EWOULDBLOCK))
|
||||
};
|
||||
}
|
||||
}
|
||||
b"connect" => {
|
||||
let new_id = self.next_id;
|
||||
let new = Handle::default();
|
||||
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
handle.require_listener()?;
|
||||
handle.connect(new_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.handles.insert(new_id, new);
|
||||
self.next_id += 1;
|
||||
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: new_id,
|
||||
flags: NewFdFlags::empty(),
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
// If a buf is provided, different than "connect" / "listen",
|
||||
// turn the socket into a named socket.
|
||||
|
||||
if buf.is_empty() {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
let path = core::str::from_utf8(buf).map_err(|_| Error::new(EBADF))?;
|
||||
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
if handle.path.is_some() {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
if matches!(handle.extra, Extra::SchemeRoot) {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
let flags = handle.flags;
|
||||
return self.open(path, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ___ ___ ___ ____ _ ___ ____ _____
|
||||
// |_ _/ _ \ ( _ ) / ___| | / _ \/ ___|| ____|
|
||||
// | | | | | / _ \/\ | | | | | | | \___ \| _|
|
||||
// | | |_| | | (_> < | |___| |__| |_| |___) | |___
|
||||
// |___\___/ \___/\/ \____|_____\___/|____/|_____|
|
||||
|
||||
fn write(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &[u8],
|
||||
_offset: u64,
|
||||
flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
let client = handle.require_client()?;
|
||||
|
||||
if let Connection::Open(remote_id) = client.remote {
|
||||
let remote = self.handles.get_mut(&remote_id).unwrap();
|
||||
match remote.extra {
|
||||
Extra::Client(ref mut client) => {
|
||||
client.buffer.extend(buf);
|
||||
if client.buffer.len() == buf.len() {
|
||||
// Send readable only if it wasn't readable
|
||||
// before
|
||||
self.post_fevent(remote_id, EVENT_READ.bits())?;
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
Extra::Listener(_) => {
|
||||
panic!("somehow, a client was connected to a listener directly")
|
||||
}
|
||||
Extra::SchemeRoot => panic!("somehow, a client was connected to a SchemeRoot"),
|
||||
}
|
||||
} else if client.remote == Connection::Closed {
|
||||
Err(Error::new(EPIPE))
|
||||
} else if (flags as usize) & O_NONBLOCK == O_NONBLOCK {
|
||||
Err(Error::new(EAGAIN))
|
||||
} else {
|
||||
Err(Error::new(EWOULDBLOCK))
|
||||
}
|
||||
}
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result<usize> {
|
||||
FpathWriter::with_legacy(buf, "chan", |w| {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
if let Extra::SchemeRoot = handle.extra {
|
||||
return Ok(());
|
||||
}
|
||||
w.push_str(handle.path.as_ref().ok_or(Error::new(EBADF))?);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
fn fsync(&mut self, id: usize, _ctx: &CallerCtx) -> Result<()> {
|
||||
self.handles.get(&id).ok_or(Error::new(EBADF)).and(Ok(()))
|
||||
}
|
||||
fn read(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &mut [u8],
|
||||
_offset: u64,
|
||||
flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
let client = handle.require_client()?;
|
||||
|
||||
if !client.buffer.is_empty() {
|
||||
let len = cmp::min(buf.len(), client.buffer.len());
|
||||
buf[..len].copy_from_slice(&client.buffer[..len]);
|
||||
client.buffer.drain(..len);
|
||||
Ok(len)
|
||||
} else if client.remote == Connection::Closed {
|
||||
// Remote dropped, send EOF
|
||||
Ok(0)
|
||||
} else if (flags as usize) & O_NONBLOCK == O_NONBLOCK {
|
||||
Err(Error::new(EAGAIN))
|
||||
} else {
|
||||
Err(Error::new(EWOULDBLOCK))
|
||||
}
|
||||
}
|
||||
fn on_close(&mut self, id: usize) {
|
||||
let handle = self
|
||||
.handles
|
||||
.remove(&id)
|
||||
.expect("handle pointing to nothing");
|
||||
|
||||
match handle.extra {
|
||||
Extra::Client(client) => {
|
||||
if let Connection::Open(remote_id) = client.remote {
|
||||
let remote = self.handles.get_mut(&remote_id).unwrap();
|
||||
|
||||
match remote.extra {
|
||||
Extra::Client(ref mut client) => {
|
||||
client.remote = Connection::Closed;
|
||||
if client.buffer.is_empty() {
|
||||
// Post readable on EOF only if it wasn't
|
||||
// readable before
|
||||
self.post_fevent(remote_id, EVENT_READ.bits()).unwrap();
|
||||
}
|
||||
}
|
||||
Extra::Listener(_) => panic!("a client can't be connected to a listener!"),
|
||||
Extra::SchemeRoot => {
|
||||
panic!("a client can't be connected to a scheme root!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Extra::Listener(listener) => {
|
||||
if let Some(path) = listener.path {
|
||||
self.listeners.remove(&path);
|
||||
}
|
||||
}
|
||||
Extra::SchemeRoot => {}
|
||||
}
|
||||
}
|
||||
|
||||
// ____ _ ____ _ __ __ _____ _____ _____ ____ ____
|
||||
// | _ \ / \ | _ \ / \ | \/ | ____|_ _| ____| _ \/ ___|
|
||||
// | |_) / _ \ | |_) | / _ \ | |\/| | _| | | | _| | |_) \___ \
|
||||
// | __/ ___ \| _ < / ___ \| | | | |___ | | | |___| _ < ___) |
|
||||
// |_| /_/ \_\_| \_\/_/ \_\_| |_|_____| |_| |_____|_| \_\____/
|
||||
|
||||
fn fcntl(&mut self, id: usize, cmd: usize, arg: usize, _ctx: &CallerCtx) -> Result<usize> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
match cmd {
|
||||
F_GETFL => Ok(handle.flags),
|
||||
F_SETFL => {
|
||||
handle.flags = arg;
|
||||
Ok(0)
|
||||
}
|
||||
_ => Err(Error::new(EINVAL)),
|
||||
}
|
||||
}
|
||||
fn fevent(&mut self, id: usize, _flags: EventFlags, _ctx: &CallerCtx) -> Result<EventFlags> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
let mut events = EventFlags::empty();
|
||||
match handle.extra {
|
||||
Extra::Client(ref client) => {
|
||||
if let Connection::Open(_) = client.remote {
|
||||
events |= EVENT_WRITE;
|
||||
}
|
||||
if !client.buffer.is_empty() || client.remote == Connection::Closed {
|
||||
events |= EVENT_READ;
|
||||
}
|
||||
}
|
||||
Extra::Listener(ref listener) => {
|
||||
if !listener.awaiting.is_empty() {
|
||||
events |= EVENT_READ | EVENT_WRITE;
|
||||
}
|
||||
}
|
||||
Extra::SchemeRoot => {}
|
||||
}
|
||||
Ok(events)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
use event::{EventFlags, EventQueue};
|
||||
use redox_scheme::scheme::register_sync_scheme;
|
||||
use redox_scheme::Socket;
|
||||
use scheme_utils::ReadinessBased;
|
||||
|
||||
mod chan;
|
||||
mod shm;
|
||||
mod uds;
|
||||
|
||||
use self::chan::ChanScheme;
|
||||
use self::shm::ShmScheme;
|
||||
use self::uds::dgram::UdsDgramScheme;
|
||||
use self::uds::stream::UdsStreamScheme;
|
||||
|
||||
fn main() {
|
||||
daemon::Daemon::new(daemon_runner);
|
||||
}
|
||||
|
||||
fn daemon_runner(daemon: daemon::Daemon) -> ! {
|
||||
// TODO: Better error handling
|
||||
match inner(daemon) {
|
||||
Ok(()) => std::process::exit(0),
|
||||
Err(error) => {
|
||||
println!("ipcd failed: {error}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn inner(daemon: daemon::Daemon) -> anyhow::Result<()> {
|
||||
event::user_data! {
|
||||
enum EventSource {
|
||||
ChanSocket,
|
||||
ShmSocket,
|
||||
UdsStreamSocket,
|
||||
UdsDgramSocket,
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare chan scheme
|
||||
let chan_socket =
|
||||
Socket::nonblock().map_err(|e| anyhow::anyhow!("failed to create chan scheme: {e}"))?;
|
||||
let mut chan = ChanScheme::new(&chan_socket);
|
||||
let mut chan_handler = ReadinessBased::new(&chan_socket, 16);
|
||||
|
||||
// Prepare shm scheme
|
||||
let shm_socket =
|
||||
Socket::nonblock().map_err(|e| anyhow::anyhow!("failed to create shm socket: {e}"))?;
|
||||
let mut shm = ShmScheme::new();
|
||||
let mut shm_handler = ReadinessBased::new(&shm_socket, 16);
|
||||
|
||||
// Prepare uds stream scheme
|
||||
let uds_stream_socket = Socket::nonblock()
|
||||
.map_err(|e| anyhow::anyhow!("failed to create uds stream scheme: {e}"))?;
|
||||
let mut uds_stream = UdsStreamScheme::new(&uds_stream_socket)
|
||||
.map_err(|e| anyhow::anyhow!("failed to create uds stream scheme: {e}"))?;
|
||||
let mut uds_stream_handler = ReadinessBased::new(&uds_stream_socket, 16);
|
||||
|
||||
// Prepare uds dgram scheme
|
||||
let uds_dgram_socket = Socket::nonblock()
|
||||
.map_err(|e| anyhow::anyhow!("failed to create uds dgram scheme: {e}"))?;
|
||||
let mut uds_dgram = UdsDgramScheme::new(&uds_dgram_socket)
|
||||
.map_err(|e| anyhow::anyhow!("failed to create uds dgram scheme: {e}"))?;
|
||||
let mut uds_dgram_handler = ReadinessBased::new(&uds_dgram_socket, 16);
|
||||
|
||||
register_sync_scheme(&chan_socket, "chan", &mut chan)
|
||||
.map_err(|e| anyhow::anyhow!("failed to register chan scheme: {e}"))?;
|
||||
register_sync_scheme(&shm_socket, "shm", &mut shm)
|
||||
.map_err(|e| anyhow::anyhow!("failed to register shm scheme: {e}"))?;
|
||||
register_sync_scheme(&uds_stream_socket, "uds_stream", &mut uds_stream)
|
||||
.map_err(|e| anyhow::anyhow!("failed to register uds stream scheme: {e}"))?;
|
||||
register_sync_scheme(&uds_dgram_socket, "uds_dgram", &mut uds_dgram)
|
||||
.map_err(|e| anyhow::anyhow!("failed to register uds dgram scheme: {e}"))?;
|
||||
|
||||
daemon.ready();
|
||||
|
||||
// Create event listener for both files
|
||||
let event_queue = EventQueue::<EventSource>::new()
|
||||
.map_err(|e| anyhow::anyhow!("failed to create event queue: {e}"))?;
|
||||
event_queue
|
||||
.subscribe(
|
||||
chan_socket.inner().raw(),
|
||||
EventSource::ChanSocket,
|
||||
EventFlags::READ,
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("failed to subscribe chan socket: {e}"))?;
|
||||
event_queue
|
||||
.subscribe(
|
||||
shm_socket.inner().raw(),
|
||||
EventSource::ShmSocket,
|
||||
EventFlags::READ,
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("failed to subscribe shm socket: {e}"))?;
|
||||
event_queue
|
||||
.subscribe(
|
||||
uds_stream_socket.inner().raw(),
|
||||
EventSource::UdsStreamSocket,
|
||||
EventFlags::READ,
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("failed to subscribe uds stream socket: {e}"))?;
|
||||
event_queue
|
||||
.subscribe(
|
||||
uds_dgram_socket.inner().raw(),
|
||||
EventSource::UdsDgramSocket,
|
||||
EventFlags::READ,
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("failed to subscribe uds dgram socket: {e}"))?;
|
||||
|
||||
libredox::call::setrens(0, 0)?;
|
||||
|
||||
loop {
|
||||
let event = event_queue
|
||||
.next_event()
|
||||
.map_err(|e| anyhow::anyhow!("error occured in event queue: {e}"))?;
|
||||
|
||||
match event.user_data {
|
||||
EventSource::ChanSocket => {
|
||||
// Channel scheme
|
||||
chan_handler.read_and_process_requests(&mut chan)?;
|
||||
chan_handler
|
||||
.poll_all_requests(&mut chan)
|
||||
.map_err(|e| anyhow::anyhow!("error occured in poll_all_requests: {e}"))?;
|
||||
chan_handler.write_responses()?;
|
||||
}
|
||||
EventSource::ShmSocket => {
|
||||
// Shared memory scheme
|
||||
shm_handler.read_and_process_requests(&mut shm)?;
|
||||
// shm is not a blocking scheme
|
||||
shm_handler.write_responses()?;
|
||||
}
|
||||
EventSource::UdsStreamSocket => {
|
||||
// Unix Domain Socket Stream scheme
|
||||
uds_stream_handler.read_and_process_requests(&mut uds_stream)?;
|
||||
uds_stream_handler
|
||||
.poll_all_requests(&mut uds_stream)
|
||||
.map_err(|e| anyhow::anyhow!("error occured in poll_all_requests: {e}"))?;
|
||||
uds_stream_handler.write_responses()?;
|
||||
}
|
||||
EventSource::UdsDgramSocket => {
|
||||
// Unix Domain Socket Dgram scheme
|
||||
uds_dgram_handler.read_and_process_requests(&mut uds_dgram)?;
|
||||
uds_dgram_handler
|
||||
.poll_all_requests(&mut uds_dgram)
|
||||
.map_err(|e| anyhow::anyhow!("error occured in poll_all_requests: {e}"))?;
|
||||
uds_dgram_handler.write_responses()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
use redox_scheme::{scheme::SchemeSync, CallerCtx, OpenResult};
|
||||
use scheme_utils::{FpathWriter, HandleMap};
|
||||
use std::{
|
||||
cmp,
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
rc::Rc,
|
||||
};
|
||||
use syscall::{
|
||||
data::Stat, error::*, schemev2::NewFdFlags, Error, Map, MapFlags, MremapFlags, Result,
|
||||
MAP_PRIVATE, PAGE_SIZE, PROT_READ, PROT_WRITE,
|
||||
};
|
||||
|
||||
enum Handle {
|
||||
Shm(Rc<str>),
|
||||
SchemeRoot,
|
||||
}
|
||||
impl Handle {
|
||||
fn as_shm(&self) -> Result<&Rc<str>, Error> {
|
||||
match self {
|
||||
Self::Shm(path) => Ok(path),
|
||||
Self::SchemeRoot => Err(Error::new(EBADF)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Move to relibc
|
||||
const AT_REMOVEDIR: usize = 0x200;
|
||||
|
||||
pub struct ShmHandle {
|
||||
buffer: MmapGuard,
|
||||
refs: usize,
|
||||
unlinked: bool,
|
||||
}
|
||||
pub struct ShmScheme {
|
||||
maps: HashMap<Rc<str>, ShmHandle>,
|
||||
handles: HandleMap<Handle>,
|
||||
}
|
||||
impl ShmScheme {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
maps: HashMap::new(),
|
||||
handles: HandleMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeSync for ShmScheme {
|
||||
fn scheme_root(&mut self) -> Result<usize> {
|
||||
Ok(self.handles.insert(Handle::SchemeRoot))
|
||||
}
|
||||
//FIXME: Handle O_RDONLY/O_WRONLY/O_RDWR
|
||||
fn openat(
|
||||
&mut self,
|
||||
dirfd: usize,
|
||||
path: &str,
|
||||
flags: usize,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<OpenResult> {
|
||||
let handle = self.handles.get(dirfd)?;
|
||||
if !matches!(handle, Handle::SchemeRoot) {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
|
||||
let path = Rc::from(path);
|
||||
let entry = match self.maps.entry(Rc::clone(&path)) {
|
||||
Entry::Occupied(e) => {
|
||||
if flags & syscall::O_EXCL != 0 && flags & syscall::O_CREAT != 0 {
|
||||
return Err(Error::new(EEXIST));
|
||||
}
|
||||
e.into_mut()
|
||||
}
|
||||
Entry::Vacant(e) => {
|
||||
if flags & syscall::O_CREAT == 0 {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
e.insert(ShmHandle {
|
||||
buffer: MmapGuard::new(),
|
||||
refs: 0,
|
||||
unlinked: false,
|
||||
})
|
||||
}
|
||||
};
|
||||
entry.refs += 1;
|
||||
let id = self.handles.insert(Handle::Shm(path));
|
||||
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::POSITIONED,
|
||||
})
|
||||
}
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result<usize> {
|
||||
FpathWriter::with(buf, "shm", |w| {
|
||||
w.push_str(self.handles.get(id).and_then(Handle::as_shm)?);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
fn on_close(&mut self, id: usize) {
|
||||
let Handle::Shm(path) = self.handles.remove(id).unwrap() else {
|
||||
return;
|
||||
};
|
||||
let mut entry = match self.maps.entry(path) {
|
||||
Entry::Occupied(entry) => entry,
|
||||
Entry::Vacant(_) => panic!("handle pointing to nothing"),
|
||||
};
|
||||
entry.get_mut().refs -= 1;
|
||||
if entry.get().refs == 0 && entry.get().unlinked {
|
||||
// There is no other reference to this entry, drop
|
||||
entry.remove_entry();
|
||||
}
|
||||
}
|
||||
fn unlinkat(&mut self, dirfd: usize, path: &str, flags: usize, _ctx: &CallerCtx) -> Result<()> {
|
||||
let handle = self.handles.get(dirfd)?;
|
||||
if !matches!(handle, Handle::SchemeRoot) {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
if flags & AT_REMOVEDIR == AT_REMOVEDIR {
|
||||
return Err(Error::new(ENOTDIR));
|
||||
}
|
||||
let path = Rc::from(path);
|
||||
let mut entry = match self.maps.entry(Rc::clone(&path)) {
|
||||
Entry::Occupied(e) => e,
|
||||
Entry::Vacant(_) => return Err(Error::new(ENOENT)),
|
||||
};
|
||||
|
||||
entry.get_mut().unlinked = true;
|
||||
if entry.get().refs == 0 {
|
||||
// There is no other reference to this entry, drop
|
||||
entry.remove_entry();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn fstat(&mut self, id: usize, stat: &mut Stat, _ctx: &CallerCtx) -> Result<()> {
|
||||
let path = self.handles.get(id).and_then(Handle::as_shm)?;
|
||||
let size = self
|
||||
.maps
|
||||
.get(path)
|
||||
.expect("handle pointing to nothing")
|
||||
.buffer
|
||||
.len();
|
||||
|
||||
//TODO: fill in more items?
|
||||
*stat = Stat {
|
||||
st_mode: syscall::MODE_FILE,
|
||||
st_size: size as _,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn fsize(&mut self, id: usize, _ctx: &CallerCtx) -> Result<u64> {
|
||||
let path = self.handles.get(id).and_then(Handle::as_shm)?;
|
||||
let size = self
|
||||
.maps
|
||||
.get(path)
|
||||
.expect("handle pointing to nothing")
|
||||
.buffer
|
||||
.len();
|
||||
|
||||
Ok(size as u64)
|
||||
}
|
||||
fn ftruncate(&mut self, id: usize, len: u64, _ctx: &CallerCtx) -> Result<()> {
|
||||
let path = self.handles.get(id).and_then(Handle::as_shm)?;
|
||||
self.maps
|
||||
.get_mut(path)
|
||||
.expect("handle pointing to nothing")
|
||||
.buffer
|
||||
.grow_to(len as usize)
|
||||
}
|
||||
fn mmap_prep(
|
||||
&mut self,
|
||||
id: usize,
|
||||
offset: u64,
|
||||
size: usize,
|
||||
_flags: MapFlags,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
let path = self.handles.get(id).and_then(Handle::as_shm)?;
|
||||
self.maps
|
||||
.get_mut(path)
|
||||
.expect("handle pointing to nothing")
|
||||
.buffer
|
||||
.mmap(offset as usize, size)
|
||||
}
|
||||
fn read(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &mut [u8],
|
||||
offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
let path = self.handles.get(id).and_then(Handle::as_shm)?;
|
||||
self.maps
|
||||
.get_mut(path)
|
||||
.expect("handle pointing to nothing")
|
||||
.buffer
|
||||
.read(offset as usize, buf)
|
||||
}
|
||||
fn write(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &[u8],
|
||||
offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
let path = self.handles.get(id).and_then(Handle::as_shm)?;
|
||||
self.maps
|
||||
.get_mut(path)
|
||||
.expect("handle pointing to nothing")
|
||||
.buffer
|
||||
.write(offset as usize, buf)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MmapGuard {
|
||||
base: *mut (),
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl MmapGuard {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: core::ptr::null_mut(),
|
||||
len: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn grow_to(&mut self, new_len: usize) -> Result<()> {
|
||||
if new_len <= self.total_capacity() {
|
||||
// FIXME clear bytes after new_len
|
||||
self.len = new_len;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let needed = new_len - self.total_capacity();
|
||||
let page_count = needed.div_ceil(PAGE_SIZE);
|
||||
let alloc_size = page_count * PAGE_SIZE;
|
||||
|
||||
let new_base = unsafe {
|
||||
if self.base.is_null() {
|
||||
syscall::fmap(
|
||||
!0,
|
||||
&Map {
|
||||
offset: 0,
|
||||
size: alloc_size,
|
||||
flags: MAP_PRIVATE | PROT_READ | PROT_WRITE,
|
||||
address: 0,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
syscall::syscall5(
|
||||
syscall::SYS_MREMAP,
|
||||
self.base as usize,
|
||||
self.len.next_multiple_of(PAGE_SIZE),
|
||||
0,
|
||||
new_len.next_multiple_of(PAGE_SIZE),
|
||||
MremapFlags::empty().bits() | (PROT_READ | PROT_WRITE).bits(),
|
||||
)
|
||||
}
|
||||
}?;
|
||||
|
||||
self.base = new_base as *mut ();
|
||||
self.len = new_len;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn total_capacity(&self) -> usize {
|
||||
self.len.next_multiple_of(PAGE_SIZE)
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
|
||||
pub fn mmap(&mut self, offset: usize, size: usize) -> Result<usize> {
|
||||
let total_size = offset + size;
|
||||
|
||||
if total_size > self.len.next_multiple_of(PAGE_SIZE) {
|
||||
return Err(Error::new(ERANGE));
|
||||
}
|
||||
|
||||
if size == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
Ok(self.base.addr() + offset)
|
||||
}
|
||||
|
||||
pub fn read(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
if offset >= self.len {
|
||||
// FIXME read as zeros
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let to_read = cmp::min(self.len - offset, buf.len());
|
||||
unsafe {
|
||||
let src = (self.base as *const u8).add(offset);
|
||||
let dst = buf.as_mut_ptr();
|
||||
core::ptr::copy_nonoverlapping(src, dst, to_read);
|
||||
}
|
||||
|
||||
Ok(to_read)
|
||||
}
|
||||
|
||||
pub fn write(&mut self, offset: usize, buf: &[u8]) -> Result<usize> {
|
||||
let end = offset.checked_add(buf.len()).ok_or(Error::new(ERANGE))?;
|
||||
self.grow_to(end)?;
|
||||
|
||||
let to_write = cmp::min(self.len - offset, buf.len());
|
||||
unsafe {
|
||||
let src = buf.as_ptr();
|
||||
let dst = (self.base as *mut u8).add(offset);
|
||||
core::ptr::copy_nonoverlapping(src, dst, to_write);
|
||||
}
|
||||
|
||||
Ok(to_write)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MmapGuard {
|
||||
fn drop(&mut self) {
|
||||
if !self.base.is_null() {
|
||||
let _ =
|
||||
unsafe { syscall::funmap(self.base.addr(), self.len.next_multiple_of(PAGE_SIZE)) };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user