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