Newer
Older
#![no_std]
extern crate alloc;
use alloc::format;
use core::marker::PhantomData;
Error, EventFlags, MapFlags, MunmapFlags, Packet, Result, Stat, StatVfs, TimeSpec, EBADF, EINTR, ENOENT, EOPNOTSUPP, ESKMSG, KSMSG_CANCEL, KSMSG_MMAP, KSMSG_MSYNC, KSMSG_MUNMAP, O_FSYNC, SKMSG_FRETURNFD
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
pub use self::scheme::Scheme;
pub use self::scheme_block::SchemeBlock;
pub use self::scheme_block_mut::SchemeBlockMut;
pub use self::scheme_mut::SchemeMut;
pub use self::seek::*;
unsafe fn str_from_raw_parts(ptr: *const u8, len: usize) -> Option<&'static str> {
let slice = slice::from_raw_parts(ptr, len);
str::from_utf8(slice).ok()
}
mod scheme;
mod scheme_block;
mod scheme_block_mut;
mod scheme_mut;
mod seek;
pub struct CallerCtx {
pub pid: usize,
pub uid: u32,
pub gid: u32,
}
pub enum OpenResult {
ThisScheme { number: usize },
OtherScheme { fd: usize },
}
// TODO: Find a better solution than generate.sh
pub(crate) fn convert_to_this_scheme(r: Result<usize>) -> Result<OpenResult> {
r.map(|number| OpenResult::ThisScheme { number })
}
pub(crate) fn convert_to_this_scheme_block(r: Result<Option<usize>>) -> Result<Option<OpenResult>> {
r.map(|o| o.map(|number| OpenResult::ThisScheme { number }))
}
pub(crate) fn convert_in_scheme_handle_block(
_: &Packet,
result: Result<Option<OpenResult>>,
) -> Result<Option<usize>> {
match result {
Ok(Some(OpenResult::ThisScheme { number })) => Ok(Some(number)),
Ok(Some(OpenResult::OtherScheme { .. })) => Err(Error::new(EOPNOTSUPP)),
Ok(None) => Ok(None),
Err(err) => Err(err),
}
}
pub(crate) fn convert_in_scheme_handle(
packet: &mut Packet,
result: Result<OpenResult>,
) -> Result<usize> {
match result {
Ok(OpenResult::ThisScheme { number }) => Ok(number),
Ok(OpenResult::OtherScheme { fd }) => {
packet.b = SKMSG_FRETURNFD;
packet.c = fd;
Err(Error::new(ESKMSG))
}
Err(err) => Err(err),
}
}
impl CallerCtx {
pub fn from_packet(packet: &Packet) -> Self {
Self {
pid: packet.pid,
uid: packet.uid,
gid: packet.gid,
}
}
}
use core::mem::size_of;
#[repr(transparent)]
pub struct Request<V: Version = V2> {
sqe: V::RawSqe,
}
mod private {
use core::ops::Deref;
use super::*;
pub trait Impl: Copy + Clone + core::fmt::Debug + Default {
type RawSqe: core::fmt::Debug + Clone + Copy + Default;
type RawCqe: core::fmt::Debug + Clone + Copy + Default + Deref<Target = [u8]>;
const OPEN_FLAGS: i32 = 0;
fn sqe_context_id(s: &Self::RawSqe) -> usize;
fn sqe_request_id(s: &Self::RawSqe) -> Id;
fn sqe_kind(s: Self::RawSqe) -> RequestKind<Self>;
fn create_cqe(req: &CallRequest<Self>, status: Result<usize>) -> Self::RawCqe;
fn post_fevent_cqe(fd: usize, flags: usize) -> Self::RawCqe;
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
}
}
pub trait Version: private::Impl {}
#[derive(Clone, Copy, Debug)]
pub enum Legacy {}
#[derive(Clone, Copy, Debug)]
pub enum V2 {}
impl Default for Legacy {
fn default() -> Self {
unreachable!()
}
}
impl Default for V2 {
fn default() -> Self {
unreachable!()
}
}
impl<V: private::Impl> Version for V {}
impl private::Impl for Legacy {
type RawSqe = Packet;
type RawCqe = Packet;
fn sqe_context_id(s: &Packet) -> usize {
s.pid
}
fn sqe_request_id(s: &Packet) -> Id {
Id(s.id as u32)
}
fn sqe_kind(packet: Self::RawSqe) -> RequestKind<Self> {
// TODO: Should KSMSG_MMAP_PREP be a regular scheme call?
match packet.a {
KSMSG_CANCEL => RequestKind::Cancellation(CancellationRequest {
id: Id(packet.b as u32),
}),
KSMSG_MSYNC => RequestKind::MsyncMsg,
//KSMSG_MUNMAP => RequestKind::MunmapMsg,
KSMSG_MMAP => RequestKind::MmapMsg,
_ => RequestKind::Call(CallRequest { inner: Request { sqe: packet } }),
}
}
fn create_cqe(req: &CallRequest<Self>, status: Result<usize>) -> Packet {
Packet {
a: Error::mux(status),
id: req.inner.sqe.id,
..Packet::default()
}
}
fn post_fevent_cqe(fd: usize, flags: usize) -> Packet {
syscall::Packet {
a: syscall::SYS_FEVENT,
b: fd,
c: flags,
d: 0,
..Default::default()
}
}
}
impl private::Impl for V2 {
type RawSqe = Sqe;
type RawCqe = Cqe;
const OPEN_FLAGS: i32 = O_FSYNC as i32;
fn sqe_context_id(s: &Sqe) -> usize {
s.caller as usize
}
fn sqe_request_id(s: &Sqe) -> Id {
Id(s.tag)
}
fn sqe_kind(sqe: Self::RawSqe) -> RequestKind<Self> {
match Opcode::try_from_raw(sqe.opcode) {
Some(Opcode::Cancel) => RequestKind::Cancellation(CancellationRequest {
id: Id(sqe.tag),
}),
Some(Opcode::Msync) => RequestKind::MsyncMsg,
//Some(Opcode::Munmap) => RequestKind::MunmapMsg,
Some(Opcode::RequestMmap) => RequestKind::MmapMsg,
_ => RequestKind::Call(CallRequest { inner: Request { sqe } }),
}
}
fn create_cqe(req: &CallRequest<Self>, status: Result<usize>) -> Cqe {
Cqe {
flags: CqeOpcode::RespondRegular as u8,
extra_raw: [0_u8; 3],
result: Error::mux(status) as u64,
tag: req.inner.sqe.tag,
}
}
fn post_fevent_cqe(fd: usize, flags: usize) -> Cqe {
Cqe {
flags: CqeOpcode::SendFevent as u8,
extra_raw: [0_u8; 3],
tag: flags as u32,
result: fd as u64,
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)]
pub struct CallRequest<V: Version = V2> {
inner: Request<V>,
pub enum RequestKind<V: Version = V2> {
Call(CallRequest<V>),
Cancellation(CancellationRequest),
MsyncMsg,
MunmapMsg,
MmapMsg,
}
impl CallRequest {
#[inline]
pub fn request(&self) -> Request {
self.inner
}
impl CallRequest<Legacy> {
pub fn handle_scheme(mut self, scheme: &impl Scheme) -> Response<Legacy> {
pub fn handle_scheme_mut(mut self, scheme: &mut impl SchemeMut) -> Response<Legacy> {
pub fn handle_scheme_block(self, scheme: &mut impl SchemeBlock) -> Result<Response<Legacy>, Self> {
match unsafe { scheme.handle(&self.inner.sqe) } {
Some(code) => Ok(Response::<Legacy>(Packet {
None => Err(self),
}
}
pub fn handle_scheme_block_mut(
) -> Result<Response<Legacy>, Self> {
match unsafe { scheme.handle(&self.inner.sqe) } {
Some(code) => Ok(Response::<Legacy>(Packet {
impl CallRequest<V2> {
pub fn handle_scheme_mut(self, scheme: &mut impl SchemeMut) -> Response<V2> {
let Some(opcode) = Opcode::try_from_raw(self.inner.sqe.opcode) else {
return Response::new(&self, Err(Error::new(EOPNOTSUPP)));
};
let args = self.inner.sqe.args;
let hack_uid = args[5] as u32;
let hack_gid = (args[5] >> 32) as u32;
let ctx = CallerCtx {
pid: self.inner.sqe.caller as usize,
uid: hack_uid,
gid: hack_gid,
};
let [a, b, c, _d, _e, _f] = args.map(|a| a as usize);
let result = unsafe {
use core::{str, slice};
match opcode {
Opcode::Open => match scheme.xopen(str::from_utf8_unchecked(slice::from_raw_parts(a as *const u8, b)), c, &ctx) {
Ok(OpenResult::ThisScheme { number }) => Ok(number),
Err(err) => Err(err),
Ok(OpenResult::OtherScheme { fd }) => return Response(Cqe {
tag: self.inner.sqe.tag,
extra_raw: [0_u8; 3],
flags: CqeOpcode::RespondWithFd as u8,
result: fd as u64,
}),
},
Opcode::Rmdir => scheme.rmdir(str::from_utf8_unchecked(slice::from_raw_parts(a as *const u8, b)), hack_uid, hack_gid),
Opcode::Unlink => scheme.unlink(str::from_utf8_unchecked(slice::from_raw_parts(a as *const u8, b)), hack_uid, hack_gid),
Opcode::Dup => match scheme.xdup(a, slice::from_raw_parts(b as *const u8, c), &ctx) {
Ok(OpenResult::ThisScheme { number }) => Ok(number),
Err(err) => Err(err),
Ok(OpenResult::OtherScheme { fd }) => return Response(Cqe {
tag: self.inner.sqe.tag,
extra_raw: [0_u8; 3],
flags: CqeOpcode::RespondWithFd as u8,
result: fd as u64,
}),
}
Opcode::Read => scheme.read(a, slice::from_raw_parts_mut(b as *mut u8, c)),
Opcode::Write => scheme.write(a, slice::from_raw_parts(b as *const u8, c)),
// TODO: 64-bit offset on 32-bit platforms
Opcode::Lseek => scheme.seek(a, b as isize, c).map(|o| o as usize),
Opcode::Fchmod => scheme.fchmod(a, b as u16),
Opcode::Fchown => scheme.fchown(a, b as u32, c as u32),
Opcode::Fcntl => scheme.fcntl(a, b, c),
Opcode::Fevent => scheme.fevent(a, EventFlags::from_bits_retain(b)).map(|fl| fl.bits()),
Opcode::Fpath => scheme.fpath(a, slice::from_raw_parts_mut(b as *mut u8, c)),
Opcode::Frename => scheme.frename(a, str::from_utf8_unchecked(slice::from_raw_parts(b as *const u8, c)), hack_uid, hack_gid),
Opcode::Fstat => {
assert!(c >= size_of::<Stat>());
scheme.fstat(a, &mut *(b as *mut Stat))
}
Opcode::Fstatvfs => {
assert!(c >= size_of::<StatVfs>());
scheme.fstatvfs(a, &mut *(b as *mut StatVfs))
}
Opcode::Fsync => scheme.fsync(a),
Opcode::Ftruncate => scheme.ftruncate(a, b),
Opcode::Futimens => {
assert!(c <= 2 * core::mem::size_of::<TimeSpec>());
scheme.futimens(a, slice::from_raw_parts(b as *const TimeSpec, c / core::mem::size_of::<TimeSpec>()))
}
Opcode::Close => scheme.close(a),
Opcode::MmapPrep => scheme.mmap_prep(a, args[3], b, MapFlags::from_bits_retain(c)),
Opcode::Munmap => scheme.munmap(a, args[3], b, MunmapFlags::from_bits_retain(c)),
_ => return Response::new(&self, Err(Error::new(EOPNOTSUPP))),
}
};
Response::<V2>::new(&self, result)
}
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
// TODO: Copy paste is bad, but ./generate.sh is worse
pub fn handle_scheme_block_mut(self, scheme: &mut impl SchemeBlockMut) -> Option<Response<V2>> {
let Some(opcode) = Opcode::try_from_raw(self.inner.sqe.opcode) else {
return Some(Response::new(&self, Err(Error::new(EOPNOTSUPP))));
};
let args = self.inner.sqe.args;
let hack_uid = args[5] as u32;
let hack_gid = (args[5] >> 32) as u32;
let ctx = CallerCtx {
pid: self.inner.sqe.caller as usize,
uid: hack_uid,
gid: hack_gid,
};
let [a, b, c, _d, _e, _f] = args.map(|a| a as usize);
let result = unsafe {
use core::{str, slice};
match opcode {
Opcode::Open => match scheme.xopen(str::from_utf8_unchecked(slice::from_raw_parts(a as *const u8, b)), c, &ctx).transpose()? {
Ok(OpenResult::ThisScheme { number }) => Ok(number),
Err(err) => Err(err),
Ok(OpenResult::OtherScheme { fd }) => return Some(Response(Cqe {
tag: self.inner.sqe.tag,
extra_raw: [0_u8; 3],
flags: CqeOpcode::RespondWithFd as u8,
result: fd as u64,
})),
},
Opcode::Rmdir => scheme.rmdir(str::from_utf8_unchecked(slice::from_raw_parts(a as *const u8, b)), hack_uid, hack_gid).transpose()?,
Opcode::Unlink => scheme.unlink(str::from_utf8_unchecked(slice::from_raw_parts(a as *const u8, b)), hack_uid, hack_gid).transpose()?,
Opcode::Dup => match scheme.xdup(a, slice::from_raw_parts(b as *const u8, c), &ctx).transpose()? {
Ok(OpenResult::ThisScheme { number }) => Ok(number),
Err(err) => Err(err),
Ok(OpenResult::OtherScheme { fd }) => return Some(Response(Cqe {
tag: self.inner.sqe.tag,
extra_raw: [0_u8; 3],
flags: CqeOpcode::RespondWithFd as u8,
result: fd as u64,
})),
}
Opcode::Read => scheme.read(a, slice::from_raw_parts_mut(b as *mut u8, c)).transpose()?,
Opcode::Write => scheme.write(a, slice::from_raw_parts(b as *const u8, c)).transpose()?,
// TODO: 64-bit offset on 32-bit platforms
Opcode::Lseek => scheme.seek(a, b as isize, c).transpose()?.map(|o| o as usize),
Opcode::Fchmod => scheme.fchmod(a, b as u16).transpose()?,
Opcode::Fchown => scheme.fchown(a, b as u32, c as u32).transpose()?,
Opcode::Fcntl => scheme.fcntl(a, b, c).transpose()?,
Opcode::Fevent => scheme.fevent(a, EventFlags::from_bits_retain(b)).transpose()?.map(|fl| fl.bits()),
Opcode::Fpath => scheme.fpath(a, slice::from_raw_parts_mut(b as *mut u8, c)).transpose()?,
Opcode::Frename => scheme.frename(a, str::from_utf8_unchecked(slice::from_raw_parts(b as *const u8, c)), hack_uid, hack_gid).transpose()?,
Opcode::Fstat => {
assert!(c >= size_of::<Stat>());
scheme.fstat(a, &mut *(b as *mut Stat)).transpose()?
}
Opcode::Fstatvfs => {
assert!(c >= size_of::<StatVfs>());
scheme.fstatvfs(a, &mut *(b as *mut StatVfs)).transpose()?
}
Opcode::Fsync => scheme.fsync(a).transpose()?,
Opcode::Ftruncate => scheme.ftruncate(a, b).transpose()?,
Opcode::Futimens => {
assert!(c <= 2 * core::mem::size_of::<TimeSpec>());
scheme.futimens(a, slice::from_raw_parts(b as *const TimeSpec, c / core::mem::size_of::<TimeSpec>())).transpose()?
}
Opcode::Close => scheme.close(a).transpose()?,
Opcode::MmapPrep => scheme.mmap_prep(a, args[3], b, MapFlags::from_bits_retain(c)).transpose()?,
Opcode::Munmap => scheme.munmap(a, args[3], b, MunmapFlags::from_bits_retain(c)).transpose()?,
_ => return Some(Response::new(&self, Err(Error::new(EOPNOTSUPP)))),
}
};
Some(Response::<V2>::new(&self, result))
}
pub fn kind(self) -> RequestKind<V> {
V::sqe_kind(self.sqe)
pub struct Socket<V: Version = V2> {
inner: libredox::Fd,
_marker: PhantomData<fn() -> V>,
}
impl<V: Version> Socket<V> {
fn create_inner(name: &str, nonblock: bool) -> Result<Self> {
let mut flags = V::OPEN_FLAGS;
if nonblock {
flags |= flag::O_NONBLOCK;
}
let fd = libredox::Fd::open(
&format!(":{name}"),
flag::O_CLOEXEC | flag::O_CREAT | flags,
0,
)?;
Ok(Self { inner: fd, _marker: PhantomData })
pub fn create(name: impl AsRef<str>) -> Result<Self> {
Self::create_inner(name.as_ref(), false)
}
pub fn nonblock(name: impl AsRef<str>) -> Result<Self> {
Self::create_inner(name.as_ref(), true)
}
pub fn read_requests(&self, buf: &mut [Request], behavior: SignalBehavior) -> Result<usize> {
read_requests(self.inner.raw(), buf, behavior)
}
pub fn next_request(&self, behavior: SignalBehavior) -> Result<Option<Request>> {
let mut buf = [Request::default()];
Ok(if self.read_requests(&mut buf, behavior)? > 0 {
Some(buf[0])
} else {
None
})
}
pub fn write_responses(&self, buf: &[Response], behavior: SignalBehavior) -> Result<usize> {
write_responses(self.inner.raw(), buf, behavior)
pub fn write_response(&self, resp: Response, behavior: SignalBehavior) -> Result<bool> {
Ok(self.write_responses(&[resp], behavior)? > 0)
}
pub fn post_fevent(&self, id: usize, flags: usize) -> Result<()> {
self.inner.write(&V::post_fevent_cqe(id, flags))?;
pub fn inner(&self) -> &libredox::Fd {
&self.inner
}
#[repr(transparent)]
#[derive(Clone, Copy, Default)]
impl<V: Version> Response<V> {
pub fn new(req: &CallRequest<V>, status: Result<usize>) -> Self {
Self(V::create_cqe(req, status))
}
}
pub enum SignalBehavior {
Interrupt,
Restart,
}
// TODO: Support uninitialized memory
#[inline]
behavior: SignalBehavior,
) -> Result<usize> {
let len = buf.len().checked_mul(size_of::<Request>()).unwrap();
let bytes_read = loop {
match libredox::call::read(socket, unsafe {
core::slice::from_raw_parts_mut(buf.as_mut_ptr().cast(), len)
}) {
Ok(n) => break n,
Err(error) if error.errno() == EINTR => match behavior {
SignalBehavior::Interrupt => return Err(error.into()),
}
};
debug_assert_eq!(bytes_read % size_of::<Request>(), 0);
Ok(bytes_read / size_of::<Request>())
}
#[inline]
pub fn write_responses(socket: usize, buf: &[Response], behavior: SignalBehavior) -> Result<usize> {
let bytes = unsafe {
core::slice::from_raw_parts(
buf.as_ptr().cast(),
buf.len().checked_mul(size_of::<Response>()).unwrap(),
)
};
let bytes_written = loop {
match libredox::call::write(socket, bytes) {
Ok(n) => break n,
Err(error) if error.errno() == EINTR => match behavior {
SignalBehavior::Interrupt => return Err(error.into()),
}
};
debug_assert_eq!(bytes_written % size_of::<Response>(), 0);
Ok(bytes_written / size_of::<Response>())
}