redox_scheme/
lib.rs

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