redox_scheme/
lib.rs

1#![no_std]
2
3extern crate alloc;
4use alloc::format;
5
6use core::str;
7use core::task::Poll;
8
9use libredox::flag;
10use syscall::error::{Error, Result, EINTR};
11use syscall::flag::{FobtainFdFlags, SendFdFlags};
12use syscall::schemev2::{Cqe, CqeOpcode, NewFdFlags, Opcode, Sqe};
13
14pub mod scheme;
15
16pub struct CallerCtx {
17    pub pid: usize,
18    pub uid: u32,
19    pub gid: u32,
20}
21
22pub enum OpenResult {
23    ThisScheme { number: usize, flags: NewFdFlags },
24    OtherScheme { fd: usize },
25}
26
27use core::mem::size_of;
28
29use self::scheme::IntoTag;
30
31#[repr(transparent)]
32#[derive(Debug, Default)]
33pub struct Request {
34    sqe: Sqe,
35}
36
37#[derive(Clone, Copy, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)]
38pub struct Id(u32);
39
40#[derive(Debug, Eq, Ord, Hash, PartialEq, PartialOrd)]
41pub struct Tag(Id);
42
43#[derive(Debug)]
44pub struct CancellationRequest {
45    pub id: Id,
46}
47
48#[repr(transparent)]
49#[derive(Debug)]
50pub struct CallRequest {
51    inner: Request,
52}
53
54#[repr(transparent)]
55#[derive(Debug)]
56pub struct SendFdRequest {
57    inner: Request,
58}
59
60pub enum RequestKind {
61    Call(CallRequest),
62    Cancellation(CancellationRequest),
63    SendFd(SendFdRequest),
64    MsyncMsg,
65    MunmapMsg,
66    MmapMsg,
67    OnClose { id: usize },
68}
69
70impl CallRequest {
71    #[inline]
72    pub fn request(&self) -> &Request {
73        &self.inner
74    }
75    #[inline]
76    pub fn request_id(&self) -> Id {
77        Id(self.inner.sqe.tag)
78    }
79}
80
81impl SendFdRequest {
82    #[inline]
83    pub fn request(&self) -> &Request {
84        &self.inner
85    }
86    #[inline]
87    pub fn request_id(&self) -> Id {
88        Id(self.inner.sqe.tag)
89    }
90
91    pub fn id(&self) -> usize {
92        self.inner.sqe.args[0] as usize
93    }
94
95    pub fn flags(&self) -> SendFdFlags {
96        SendFdFlags::from_bits_retain(self.inner.sqe.args[1] as usize)
97    }
98
99    pub fn obtain_fd(
100        &self,
101        socket: &Socket,
102        flags: FobtainFdFlags,
103        dst_fd_or_ptr: Result<usize, &mut usize>,
104    ) -> Result<()> {
105        assert!(!flags.contains(FobtainFdFlags::MANUAL_FD));
106        match dst_fd_or_ptr {
107            Ok(dst_fd) => {
108                socket.inner.write(&Cqe {
109                    flags: CqeOpcode::ObtainFd as u8,
110                    extra_raw: usize::to_ne_bytes((flags | FobtainFdFlags::MANUAL_FD).bits())[..3]
111                        .try_into()
112                        .unwrap(),
113                    tag: self.request_id().0,
114                    result: dst_fd as u64,
115                })?;
116            }
117            Err(ptr) => {
118                socket.inner.write(&Cqe {
119                    flags: CqeOpcode::ObtainFd as u8,
120                    extra_raw: usize::to_ne_bytes(flags.bits())[..3].try_into().unwrap(),
121                    tag: self.request_id().0,
122                    result: ptr as *mut usize as u64,
123                })?;
124            }
125        }
126        Ok(())
127    }
128}
129
130impl Request {
131    #[inline]
132    pub fn context_id(&self) -> usize {
133        self.sqe.caller as usize
134    }
135    pub fn kind(self) -> RequestKind {
136        match Opcode::try_from_raw(self.sqe.opcode) {
137            Some(Opcode::Cancel) => RequestKind::Cancellation(CancellationRequest {
138                id: Id(self.sqe.tag),
139            }),
140            Some(Opcode::Sendfd) => RequestKind::SendFd(SendFdRequest {
141                inner: Request { sqe: self.sqe },
142            }),
143            Some(Opcode::Msync) => RequestKind::MsyncMsg,
144            //Some(Opcode::Munmap) => RequestKind::MunmapMsg,
145            Some(Opcode::RequestMmap) => RequestKind::MmapMsg,
146            Some(Opcode::CloseMsg) => RequestKind::OnClose {
147                id: self.sqe.args[0] as usize,
148            },
149
150            _ => RequestKind::Call(CallRequest {
151                inner: Request { sqe: self.sqe },
152            }),
153        }
154    }
155}
156
157pub struct Socket {
158    inner: libredox::Fd,
159}
160
161impl Socket {
162    fn create_inner(name: &str, nonblock: bool) -> Result<Self> {
163        let mut flags = flag::O_FSYNC | 0x0020_0000 /* O_EXLOCK */;
164
165        if nonblock {
166            flags |= flag::O_NONBLOCK;
167        }
168
169        let fd = libredox::Fd::open(
170            &format!(":{name}"),
171            flag::O_CLOEXEC | flag::O_CREAT | flags,
172            0,
173        )?;
174        Ok(Self { inner: fd })
175    }
176    pub fn create(name: impl AsRef<str>) -> Result<Self> {
177        Self::create_inner(name.as_ref(), false)
178    }
179    pub fn nonblock(name: impl AsRef<str>) -> Result<Self> {
180        Self::create_inner(name.as_ref(), true)
181    }
182    pub fn read_requests(&self, buf: &mut [Request], behavior: SignalBehavior) -> Result<usize> {
183        read_requests(self.inner.raw(), buf, behavior)
184    }
185    pub fn next_request(&self, behavior: SignalBehavior) -> Result<Option<Request>> {
186        let mut buf = [Request::default()];
187        Ok(if self.read_requests(&mut buf, behavior)? > 0 {
188            let [req] = buf;
189            Some(req)
190        } else {
191            None
192        })
193    }
194    pub fn write_responses(&self, buf: &[Response], behavior: SignalBehavior) -> Result<usize> {
195        write_responses(self.inner.raw(), buf, behavior)
196    }
197    pub fn write_response(&self, resp: Response, behavior: SignalBehavior) -> Result<bool> {
198        Ok(self.write_responses(&[resp], behavior)? > 0)
199    }
200    pub fn post_fevent(&self, id: usize, flags: usize) -> Result<()> {
201        self.inner.write(&Cqe {
202            flags: CqeOpcode::SendFevent as u8,
203            extra_raw: [0_u8; 3],
204            tag: flags as u32,
205            result: id as u64,
206        })?;
207        Ok(())
208    }
209    pub fn inner(&self) -> &libredox::Fd {
210        &self.inner
211    }
212}
213
214#[repr(transparent)]
215#[derive(Clone, Copy, Default)]
216pub struct Response(Cqe);
217
218impl Response {
219    #[inline]
220    pub fn err(err: i32, req: impl IntoTag) -> Self {
221        Self::new(Err(Error::new(err)), req)
222    }
223    #[inline]
224    pub fn ok(status: usize, req: impl IntoTag) -> Self {
225        Self::new(Ok(status), req)
226    }
227    #[inline]
228    pub fn ready_ok(status: usize, req: impl IntoTag) -> Poll<Self> {
229        Poll::Ready(Self::ok(status, req))
230    }
231    #[inline]
232    pub fn ready_err(err: i32, req: impl IntoTag) -> Poll<Self> {
233        Poll::Ready(Self::err(err, req))
234    }
235
236    pub fn new(status: Result<usize>, req: impl IntoTag) -> Self {
237        Self(Cqe {
238            flags: CqeOpcode::RespondRegular as u8,
239            extra_raw: [0_u8; 3],
240            result: Error::mux(status) as u64,
241            tag: req.into_tag().0 .0,
242        })
243    }
244    pub fn open_dup_like(res: Result<OpenResult>, req: impl IntoTag) -> Response {
245        match res {
246            Ok(OpenResult::ThisScheme { number, flags }) => {
247                Response::new(Ok(number), req).with_extra([flags.bits(), 0, 0])
248            }
249            Err(e) => Response::new(Err(e), req),
250            Ok(OpenResult::OtherScheme { fd }) => Response::return_external_fd(fd, req),
251        }
252    }
253    pub fn return_external_fd(fd: usize, req: impl IntoTag) -> Self {
254        Self(Cqe {
255            flags: CqeOpcode::RespondWithFd as u8,
256            extra_raw: [0_u8; 3],
257            result: fd as u64,
258            tag: req.into_tag().0 .0,
259        })
260    }
261    pub fn with_extra(self, extra: [u8; 3]) -> Self {
262        Self(Cqe {
263            extra_raw: extra,
264            ..self.0
265        })
266    }
267}
268
269pub enum SignalBehavior {
270    Interrupt,
271    Restart,
272}
273
274// TODO: Support uninitialized memory
275#[inline]
276pub fn read_requests(
277    socket: usize,
278    buf: &mut [Request],
279    behavior: SignalBehavior,
280) -> Result<usize> {
281    let len = buf.len().checked_mul(size_of::<Request>()).unwrap();
282
283    let bytes_read = loop {
284        match libredox::call::read(socket, unsafe {
285            core::slice::from_raw_parts_mut(buf.as_mut_ptr().cast(), len)
286        }) {
287            Ok(n) => break n,
288            Err(error) if error.errno() == EINTR => match behavior {
289                SignalBehavior::Restart => continue,
290                SignalBehavior::Interrupt => return Err(error.into()),
291            },
292            Err(err) => return Err(err.into()),
293        }
294    };
295
296    debug_assert_eq!(bytes_read % size_of::<Request>(), 0);
297
298    Ok(bytes_read / size_of::<Request>())
299}
300
301#[inline]
302pub fn write_responses(socket: usize, buf: &[Response], behavior: SignalBehavior) -> Result<usize> {
303    let bytes = unsafe {
304        core::slice::from_raw_parts(
305            buf.as_ptr().cast(),
306            buf.len().checked_mul(size_of::<Response>()).unwrap(),
307        )
308    };
309
310    let bytes_written = loop {
311        match libredox::call::write(socket, bytes) {
312            Ok(n) => break n,
313            Err(error) if error.errno() == EINTR => match behavior {
314                SignalBehavior::Restart => continue,
315                SignalBehavior::Interrupt => return Err(error.into()),
316            },
317            Err(err) => return Err(err.into()),
318        }
319    };
320    debug_assert_eq!(bytes_written % size_of::<Response>(), 0);
321    Ok(bytes_written / size_of::<Response>())
322}