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