Skip to content
Snippets Groups Projects
lib.rs 19.5 KiB
Newer Older
#![no_std]

extern crate alloc;
use alloc::format;

use core::marker::PhantomData;
Jacob Lorentzon's avatar
Jacob Lorentzon committed
use core::{slice, str};

use libredox::flag;
Jacob Lorentzon's avatar
Jacob Lorentzon committed
use syscall::schemev2::{Cqe, CqeOpcode, Opcode, Sqe};
use syscall::{
    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
Jacob Lorentzon's avatar
Jacob Lorentzon committed

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)]
Jacob Lorentzon's avatar
Jacob Lorentzon committed
#[derive(Clone, Copy, Debug, Default)]
Jacob Lorentzon's avatar
Jacob Lorentzon committed
pub struct Request<V: Version = V2> {
    sqe: V::RawSqe,
}
mod private {
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    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;
Jacob Lorentzon's avatar
Jacob Lorentzon committed

        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;
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    }
}
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,
Jacob Lorentzon's avatar
Jacob Lorentzon committed
            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()
        }
    }
Jacob Lorentzon's avatar
Jacob Lorentzon committed
}
impl private::Impl for V2 {
    type RawSqe = Sqe;
    type RawCqe = Cqe;

    const OPEN_FLAGS: i32 = O_FSYNC as i32;

Jacob Lorentzon's avatar
Jacob Lorentzon committed
    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,
Jacob Lorentzon's avatar
Jacob Lorentzon committed
            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,
        }
    }
Jacob Lorentzon's avatar
Jacob Lorentzon committed
}
Jacob Lorentzon's avatar
Jacob Lorentzon committed

#[derive(Clone, Copy, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)]
Jacob Lorentzon's avatar
Jacob Lorentzon committed
pub struct Id(u32);

#[derive(Debug)]
Jacob Lorentzon's avatar
Jacob Lorentzon committed
pub struct CancellationRequest {
Jacob Lorentzon's avatar
Jacob Lorentzon committed
#[repr(transparent)]
#[derive(Clone, Copy, Debug)]
Jacob Lorentzon's avatar
Jacob Lorentzon committed
pub struct CallRequest<V: Version = V2> {
    inner: Request<V>,
Jacob Lorentzon's avatar
Jacob Lorentzon committed
pub enum RequestKind<V: Version = V2> {
    Call(CallRequest<V>),
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    Cancellation(CancellationRequest),
    MsyncMsg,
    MunmapMsg,
    MmapMsg,
}

impl CallRequest {
    #[inline]
    pub fn request(&self) -> Request {
        self.inner
Jacob Lorentzon's avatar
Jacob Lorentzon committed

Jacob Lorentzon's avatar
Jacob Lorentzon committed
}
impl CallRequest<Legacy> {
    pub fn handle_scheme(mut self, scheme: &impl Scheme) -> Response<Legacy> {
Jacob Lorentzon's avatar
Jacob Lorentzon committed
        unsafe {
Jacob Lorentzon's avatar
Jacob Lorentzon committed
            scheme.handle(&mut self.inner.sqe);
Jacob Lorentzon's avatar
Jacob Lorentzon committed
        }
Jacob Lorentzon's avatar
Jacob Lorentzon committed
        Response::<Legacy>(self.inner.sqe)
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    }
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    pub fn handle_scheme_mut(mut self, scheme: &mut impl SchemeMut) -> Response<Legacy> {
Jacob Lorentzon's avatar
Jacob Lorentzon committed
        unsafe {
Jacob Lorentzon's avatar
Jacob Lorentzon committed
            scheme.handle(&mut self.inner.sqe);
Jacob Lorentzon's avatar
Jacob Lorentzon committed
        }
Jacob Lorentzon's avatar
Jacob Lorentzon committed
        Response::<Legacy>(self.inner.sqe)
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    }
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    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 {
Jacob Lorentzon's avatar
Jacob Lorentzon committed
                a: code,
Jacob Lorentzon's avatar
Jacob Lorentzon committed
                ..self.inner.sqe
Jacob Lorentzon's avatar
Jacob Lorentzon committed
            })),
Jacob Lorentzon's avatar
Jacob Lorentzon committed
            None => Err(self),
        }
    }
    pub fn handle_scheme_block_mut(
Jacob Lorentzon's avatar
Jacob Lorentzon committed
        self,
Jacob Lorentzon's avatar
Jacob Lorentzon committed
        scheme: &mut impl SchemeBlockMut,
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    ) -> Result<Response<Legacy>, Self> {
        match unsafe { scheme.handle(&self.inner.sqe) } {
            Some(code) => Ok(Response::<Legacy>(Packet {
Jacob Lorentzon's avatar
Jacob Lorentzon committed
                a: code,
Jacob Lorentzon's avatar
Jacob Lorentzon committed
                ..self.inner.sqe
Jacob Lorentzon's avatar
Jacob Lorentzon committed
            })),
Jacob Lorentzon's avatar
Jacob Lorentzon committed
            None => Err(self),
        }
    }
}
    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)
    }
    // 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))
    }
Jacob Lorentzon's avatar
Jacob Lorentzon committed

Jacob Lorentzon's avatar
Jacob Lorentzon committed
impl<V: Version> Request<V> {
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    #[inline]
    pub fn context_id(&self) -> usize {
Jacob Lorentzon's avatar
Jacob Lorentzon committed
        V::sqe_context_id(&self.sqe)
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    }
    #[inline]
    pub fn request_id(&self) -> Id {
Jacob Lorentzon's avatar
Jacob Lorentzon committed
        V::sqe_request_id(&self.sqe)
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    }
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    pub fn kind(self) -> RequestKind<V> {
        V::sqe_kind(self.sqe)
pub struct Socket<V: Version = V2> {
    inner: libredox::Fd,
    _marker: PhantomData<fn() -> V>,
}
Jacob Lorentzon's avatar
Jacob Lorentzon committed

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 })
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    }
    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)
    }
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    pub fn read_requests(&self, buf: &mut [Request], behavior: SignalBehavior) -> Result<usize> {
        read_requests(self.inner.raw(), buf, behavior)
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    }
    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)
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    }
    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 {
Jacob Lorentzon's avatar
Jacob Lorentzon committed
}

#[repr(transparent)]
#[derive(Clone, Copy, Default)]
Jacob Lorentzon's avatar
Jacob Lorentzon committed
pub struct Response<V: Version = V2>(V::RawCqe);
Jacob Lorentzon's avatar
Jacob Lorentzon committed

Jacob Lorentzon's avatar
Jacob Lorentzon committed
impl<V: Version> Response<V> {
    pub fn new(req: &CallRequest<V>, status: Result<usize>) -> Self {
        Self(V::create_cqe(req, status))
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    }
}

pub enum SignalBehavior {
    Interrupt,
    Restart,
}

// TODO: Support uninitialized memory
#[inline]
Jacob Lorentzon's avatar
Jacob Lorentzon committed
pub fn read_requests<V: Version>(
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    socket: usize,
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    buf: &mut [Request<V>],
Jacob Lorentzon's avatar
Jacob Lorentzon committed
    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 {
Jacob Lorentzon's avatar
Jacob Lorentzon committed
                SignalBehavior::Restart => continue,
                SignalBehavior::Interrupt => return Err(error.into()),
Jacob Lorentzon's avatar
Jacob Lorentzon committed
            },
            Err(err) => return Err(err.into()),
Jacob Lorentzon's avatar
Jacob Lorentzon committed
        }
    };

    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 {
Jacob Lorentzon's avatar
Jacob Lorentzon committed
                SignalBehavior::Restart => continue,
                SignalBehavior::Interrupt => return Err(error.into()),
Jacob Lorentzon's avatar
Jacob Lorentzon committed
            },
            Err(err) => return Err(err.into()),
Jacob Lorentzon's avatar
Jacob Lorentzon committed
        }
    };
    debug_assert_eq!(bytes_written % size_of::<Response>(), 0);
    Ok(bytes_written / size_of::<Response>())
}