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
+3
View File
@@ -0,0 +1,3 @@
/target
**/*.rs.bk
/Xargo.toml
+26
View File
@@ -0,0 +1,26 @@
image: "redoxos/redoxer"
stages:
- build
- test
cache:
paths:
- target/
build:redox:
stage: build
script: redoxer build --verbose
test:redox:
stage: test
dependencies:
- build:redox
script:
- redoxer build --verbose --examples
- redoxer exec --folder . -- sh -- ./redoxer.sh
event
main
unnamed
shm_demo
# FIXME: shm is currently broken
+19
View File
@@ -0,0 +1,19 @@
[package]
name = "ipcd"
description = "Inter-process communication daemon"
edition = "2018"
version = "0.1.0"
[dependencies]
libredox = { workspace = true, features = ["call"]}
daemon = { path = "../daemon" }
redox_syscall.workspace = true
redox-scheme.workspace = true
scheme-utils = { path = "../scheme-utils" }
redox_event.workspace = true
anyhow.workspace = true
libc.workspace = true
rand = "0.10"
[lints]
workspace = true
+12
View File
@@ -0,0 +1,12 @@
# ipcd
A userspace daemon for interprocess communication.
## Usage
See the `examples` folder.
Simply open `chan:<name>` with O_CREAT where `<name>` is any name you'd like to create a listener.
This listener can accept clients by calling `dup("listen")`.
Open `chan:<name>` without O_CREAT to connect. Now you can read and write between both streams.
+142
View File
@@ -0,0 +1,142 @@
use std::{
fs::File,
io::{self, prelude::*},
os::unix::io::{AsRawFd, FromRawFd, RawFd},
};
fn from_syscall_error(error: syscall::Error) -> io::Error {
io::Error::from_raw_os_error(error.errno as i32)
}
fn nonblock(file: &File) -> io::Result<()> {
syscall::fcntl(
file.as_raw_fd() as usize,
syscall::F_SETFL,
syscall::O_NONBLOCK,
)
.map(|_| ())
.map_err(from_syscall_error)
}
fn dup(file: &File, buf: &str) -> io::Result<File> {
let stream =
syscall::dup(file.as_raw_fd() as usize, buf.as_bytes()).map_err(from_syscall_error)?;
Ok(unsafe { File::from_raw_fd(stream as RawFd) })
}
fn main() -> io::Result<()> {
let server = File::create("chan:hello_world")?;
nonblock(&server)?;
let mut event_file = File::open("event:")?;
let mut time_file = File::open(format!("time:{}", syscall::CLOCK_MONOTONIC))?;
let mut time = syscall::TimeSpec::default();
time_file.read(&mut time)?;
time.tv_sec += 1;
time_file.write(&time)?;
time.tv_sec += 2;
time_file.write(&time)?;
time.tv_sec += 2;
time_file.write(&time)?;
const TOKEN_TIMER: usize = 0;
const TOKEN_STREAM: usize = 1;
const TOKEN_SERVER: usize = 2;
const TOKEN_CLIENT: usize = 3;
event_file.write(&syscall::Event {
id: time_file.as_raw_fd() as usize,
flags: syscall::EVENT_READ,
data: TOKEN_TIMER,
})?;
event_file.write(&syscall::Event {
id: server.as_raw_fd() as usize,
flags: syscall::EVENT_WRITE | syscall::EVENT_READ,
data: TOKEN_SERVER,
})?;
let mut event = syscall::Event::default();
println!("Testing accept events...");
event_file.read(&mut event)?;
assert_eq!(event.data, TOKEN_TIMER);
assert_eq!(event.flags, syscall::EVENT_READ);
println!("-> Timed out");
let mut client = File::open("chan:hello_world")?;
event_file.write(&syscall::Event {
id: client.as_raw_fd() as usize,
flags: syscall::EVENT_WRITE | syscall::EVENT_READ,
data: TOKEN_CLIENT,
})?;
event_file.read(&mut event)?;
assert_eq!(event.data, TOKEN_SERVER);
assert_eq!(event.flags, syscall::EVENT_WRITE | syscall::EVENT_READ);
println!("-> Accept event");
println!("Testing write events...");
let mut stream = dup(&server, "listen")?;
event_file.read(&mut event)?;
assert_eq!(event.data, TOKEN_CLIENT);
assert_eq!(event.flags, syscall::EVENT_WRITE);
println!("-> Writable event");
event_file.write(&syscall::Event {
id: stream.as_raw_fd() as usize,
flags: syscall::EVENT_READ | syscall::EVENT_WRITE,
data: TOKEN_STREAM,
})?;
event_file.read(&mut event)?;
assert_eq!(event.data, TOKEN_STREAM);
assert_eq!(event.flags, syscall::EVENT_WRITE);
println!("-> Writable event");
event_file.read(&mut event)?;
assert_eq!(event.data, TOKEN_TIMER);
assert_eq!(event.flags, syscall::EVENT_READ);
println!("-> Timed out");
println!("Testing read events...");
client.write(b"a")?;
let mut buf = [0; 5];
event_file.read(&mut event)?;
assert_eq!(event.data, TOKEN_STREAM);
assert_eq!(event.flags, syscall::EVENT_READ);
println!("-> Readable event");
assert_eq!(stream.read(&mut buf)?, 1);
assert_eq!(buf[0], b'a');
stream.write(b"b")?;
event_file.read(&mut event)?;
assert_eq!(event.data, TOKEN_CLIENT);
assert_eq!(event.flags, syscall::EVENT_READ);
println!("-> Readable event");
assert_eq!(client.read(&mut buf)?, 1);
assert_eq!(buf[0], b'b');
drop(client);
event_file.read(&mut event)?;
assert_eq!(event.data, TOKEN_STREAM);
println!("-> Readable event (EOF)");
assert_eq!(stream.read(&mut buf)?, 0);
event_file.read(&mut event)?;
assert_eq!(event.data, TOKEN_TIMER);
println!("-> Timed out");
println!("Everything tested!");
Ok(())
}
@@ -0,0 +1,8 @@
use std::{fs::File, io};
fn main() -> io::Result<()> {
let mut client = File::open("chan:hello")?;
io::copy(&mut client, &mut io::stdout())?;
Ok(())
}
@@ -0,0 +1,21 @@
use std::{
fs::File,
io::{self, prelude::*},
os::unix::io::{AsRawFd, FromRawFd, RawFd},
};
fn from_syscall_error(error: syscall::Error) -> io::Error {
io::Error::from_raw_os_error(error.errno as i32)
}
fn main() -> io::Result<()> {
let server = File::create("chan:hello")?;
loop {
let stream =
syscall::dup(server.as_raw_fd() as usize, b"listen").map_err(from_syscall_error)?;
let mut stream = unsafe { File::from_raw_fd(stream as RawFd) };
stream.write(b"Hello World!\n")?;
}
}
@@ -0,0 +1,105 @@
use std::{
collections::HashMap,
fs::File,
io::{self, prelude::*},
os::unix::io::{AsRawFd, FromRawFd, RawFd},
str,
};
fn from_syscall_error(error: syscall::Error) -> io::Error {
io::Error::from_raw_os_error(error.errno as i32)
}
fn main() -> io::Result<()> {
let server = File::create("chan:log")?;
let mut event_file = File::open("event:")?;
syscall::fcntl(
server.as_raw_fd() as usize,
syscall::F_SETFL,
syscall::O_NONBLOCK,
)
.map(|_| ())
.map_err(from_syscall_error)?;
event_file.write(&syscall::Event {
id: server.as_raw_fd() as usize,
data: 0,
flags: syscall::EVENT_READ | syscall::EVENT_WRITE,
})?;
let mut clients = HashMap::new();
let mut next_id = 1;
'outer: loop {
let mut event = syscall::Event::default();
event_file.read(&mut event)?;
if event.data == 0 {
println!("Listener recevied flags: {:?}", event.flags);
if event.flags & syscall::EVENT_WRITE == syscall::EVENT_WRITE {
loop {
let stream = match syscall::dup(server.as_raw_fd() as usize, b"listen")
.map_err(from_syscall_error)
{
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break,
stream => stream?,
};
let stream = unsafe { File::from_raw_fd(stream as RawFd) };
event_file.write(&syscall::Event {
id: stream.as_raw_fd() as usize,
data: next_id,
flags: syscall::EVENT_READ | syscall::EVENT_WRITE,
})?;
clients.insert(next_id, stream);
println!("-> Spawned client #{}", next_id);
next_id += 1;
}
}
} else {
println!("Client #{} received flags: {:?}", event.data, event.flags);
let client = clients.get_mut(&event.data).unwrap();
if event.flags & syscall::EVENT_READ == syscall::EVENT_READ {
println!("-> Reading");
let mut buf = [0; 128];
loop {
let len = match client.read(&mut buf) {
Ok(0) => {
println!("--> EOF");
clients.remove(&event.data);
continue 'outer;
}
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break,
len => len?,
};
println!(
"--> Read {}/128 bytes: {:?}",
len,
str::from_utf8(&buf[..len])
);
}
}
if event.flags & syscall::EVENT_WRITE == syscall::EVENT_WRITE {
println!("-> Writing");
const BUF: &str = "Hello from the log server\n";
let mut written = 0;
while written < BUF.len() {
let len = match client.write(BUF[written..].as_bytes()) {
Ok(0) => panic!("EOF should never happen here"),
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break,
len => len?,
};
println!(
"--> Wrote {}/{} bytes: {:?}",
len,
BUF.len(),
&BUF[written..]
);
written += len;
}
}
}
}
}
+135
View File
@@ -0,0 +1,135 @@
use std::{
fs::{File, OpenOptions},
io::{self, prelude::*},
os::unix::io::{AsRawFd, FromRawFd, RawFd},
thread,
time::Duration,
};
fn from_syscall_error(error: syscall::Error) -> io::Error {
io::Error::from_raw_os_error(error.errno as i32)
}
fn nonblock(file: &File) -> io::Result<()> {
syscall::fcntl(
file.as_raw_fd() as usize,
syscall::F_SETFL,
syscall::O_NONBLOCK,
)
.map(|_| ())
.map_err(from_syscall_error)
}
fn dup(file: &File, buf: &str) -> io::Result<File> {
let stream =
syscall::dup(file.as_raw_fd() as usize, buf.as_bytes()).map_err(from_syscall_error)?;
Ok(unsafe { File::from_raw_fd(stream as RawFd) })
}
fn main() -> io::Result<()> {
let mut buf = [0; 5];
let server = File::create("chan:hello_world")?;
{
println!("Testing O_EXCL...");
assert_eq!(
OpenOptions::new()
.write(true)
.create_new(true)
.open("chan:hello_world")
.unwrap_err()
.kind(),
io::ErrorKind::AlreadyExists
);
println!("Testing connecting...");
File::open("chan:hello_world")?; // closed connection will silently be skipped
let mut client = File::create("chan:hello_world")?; // O_CREAT without O_EXCL does nothing
let tmp = File::open("chan:hello_world")?; // multiple connections are handled
let mut stream = dup(&server, "listen")?;
assert!(dup(&server, "listen").is_ok());
drop(tmp);
println!("Testing basic I/O...");
stream.write(b"abc")?;
stream.flush()?;
println!("-> Wrote message");
assert_eq!(client.read(&mut buf)?, 3);
assert_eq!(&buf[..3], b"abc");
println!("-> Read message");
println!("Testing close...");
drop(client);
assert_eq!(
stream.write(b"a").unwrap_err().kind(),
io::ErrorKind::BrokenPipe
);
assert_eq!(stream.read(&mut buf)?, 0);
}
println!("Testing alternative connect method...");
let mut client = dup(&server, "connect")?;
let mut stream = dup(&server, "listen")?;
println!("Testing blocking I/O...");
let mut client_clone = client.try_clone()?;
let thread = thread::spawn(move || -> io::Result<()> {
println!("--> Thread: Sleeping for 1 second...");
thread::sleep(Duration::from_secs(1));
println!("--> Thread: Writing...");
client_clone.write(b"def")?;
client_clone.flush()?;
Ok(())
});
assert_eq!(stream.read(&mut buf)?, 3);
assert_eq!(&buf[..3], b"def");
println!("-> Read message");
thread.join().unwrap().unwrap();
println!("Testing non-blocking I/O...");
nonblock(&client)?;
nonblock(&server)?;
assert_eq!(
client.read(&mut buf).unwrap_err().kind(),
io::ErrorKind::WouldBlock
);
println!("-> Read would block");
assert_eq!(
dup(&server, "listen").unwrap_err().kind(),
io::ErrorKind::WouldBlock
);
println!("-> Accept would block");
drop(client);
{
let mut client = File::open("chan:hello_world")?;
nonblock(&client)?;
assert_eq!(
client.write(b"a").unwrap_err().kind(),
io::ErrorKind::WouldBlock
);
println!("-> Write before accept would block");
}
let mut client = dup(&server, "connect")?;
nonblock(&client)?;
assert_eq!(
client.write(b"a").unwrap_err().kind(),
io::ErrorKind::WouldBlock
);
println!("-> Write before accept would block (alternative connection method)");
println!("Everything tested!");
Ok(())
}
+56
View File
@@ -0,0 +1,56 @@
use std::{fs::File, io, os::unix::io::AsRawFd, slice};
fn from_syscall_error(error: syscall::Error) -> io::Error {
io::Error::from_raw_os_error(error.errno as i32)
}
fn main() -> Result<(), io::Error> {
let file1 = File::open("shm:example")?;
let file2 = File::open("shm:example")?;
let one = unsafe {
slice::from_raw_parts_mut(
syscall::fmap(
file1.as_raw_fd() as usize,
&syscall::Map {
offset: 0,
size: 128,
flags: syscall::PROT_READ | syscall::PROT_WRITE | syscall::MAP_SHARED,
address: 0,
},
)
.map_err(from_syscall_error)? as *mut u8,
128,
)
};
// FIXME: While the length can be unaligned, the offset cannot. This test is incorrectly
// written.
let two = unsafe {
slice::from_raw_parts_mut(
syscall::fmap(
file2.as_raw_fd() as usize,
&syscall::Map {
offset: 64,
size: 64,
flags: syscall::PROT_READ | syscall::PROT_WRITE | syscall::MAP_SHARED,
address: 0,
},
)
.map_err(from_syscall_error)? as *mut u8,
64,
)
};
println!("Testing writing between");
for i in 0..128 {
one[i as usize] = i;
}
for i in 0..64 {
assert_eq!(two[i as usize], 64 + i);
}
println!("Testing fpath");
let mut buf = [0; 128];
let len = syscall::fpath(file1.as_raw_fd() as usize, &mut buf).map_err(from_syscall_error)?;
assert_eq!(&buf[..len], b"shm:example");
Ok(())
}
@@ -0,0 +1,27 @@
use std::{fs::File, io, mem, os::unix::io::AsRawFd, thread, time::Duration};
fn from_syscall_error(error: syscall::Error) -> io::Error {
io::Error::from_raw_os_error(error.errno as i32)
}
fn main() -> Result<(), io::Error> {
let file = File::open("shm:counter")?;
println!("Reading from map... ");
let counter = unsafe {
&mut *(syscall::fmap(
file.as_raw_fd() as usize,
&syscall::Map {
offset: 0,
address: 0,
size: mem::size_of::<usize>(),
flags: syscall::PROT_READ | syscall::PROT_WRITE | syscall::MAP_SHARED,
},
)
.map_err(from_syscall_error)? as *mut usize)
};
println!("Read value {}", counter);
*counter += 1;
println!("Increased value to {}", counter);
thread::sleep(Duration::from_secs(1));
Ok(())
}
@@ -0,0 +1,41 @@
use std::{
fs::File,
io::{self, prelude::*},
os::unix::io::{AsRawFd, FromRawFd, RawFd},
};
fn from_syscall_error(error: syscall::Error) -> io::Error {
io::Error::from_raw_os_error(error.errno as i32)
}
fn dup(file: &File, buf: &str) -> io::Result<File> {
let stream =
syscall::dup(file.as_raw_fd() as usize, buf.as_bytes()).map_err(from_syscall_error)?;
Ok(unsafe { File::from_raw_fd(stream as RawFd) })
}
fn main() -> io::Result<()> {
let mut buf = [0; 5];
let server = File::create("chan:")?;
let mut client = dup(&server, "connect")?;
let mut stream = dup(&server, "listen")?;
println!("Testing basic I/O...");
stream.write(b"abc")?;
stream.flush()?;
println!("-> Wrote message");
assert_eq!(client.read(&mut buf)?, 3);
assert_eq!(&buf[..3], b"abc");
println!("-> Read message");
println!("Testing connecting to unnamed socket by name (makes no sense)...");
assert_eq!(
File::open("chan:").unwrap_err().kind(),
io::ErrorKind::NotFound
);
println!("Everything tested!");
Ok(())
}
+20
View File
@@ -0,0 +1,20 @@
#!/bin/sh
set -e
old_path="file:/bin/ipcd"
new_path="target/x86_64-unknown-redox/debug/ipcd"
if [ -e "${new_path}" ]
then
mv -v "${new_path}" "${old_path}"
shutdown --reboot
fi
while [ "$#" != "0" ]
do
example="$1"
shift
echo "# ${example} #"
"target/x86_64-unknown-redox/debug/examples/${example}"
done
+446
View File
@@ -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)
}
}
+149
View File
@@ -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()?;
}
}
}
}
+329
View File
@@ -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)) };
}
}
}
+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