jay-compositor 1.15.0

The Jay compositor
use crate::async_engine::AsyncEngine;
use crate::async_engine::SpawnedFuture;
use crate::forker::ForkerProxy;
use crate::io_uring::IoUring;
use crate::utils::buf::Buf;
use crate::utils::errorfmt::ErrorFmt;
use crate::utils::fuse::fuse_error::FuseError;
use crate::utils::fuse::fuse_inode::FuseInode;
use crate::utils::fuse::fuse_inode::FuseInodeTy;
use crate::utils::fuse::fuse_inode_cache::InodeCache;
use crate::utils::fuse::fuse_mgr::FuseMgr;
use crate::utils::fuse::fuse_mount::FuseMountEarlyShared;
use crate::utils::fuse::fuse_mount::FuseMountOwnerHolder;
use crate::utils::fuse::fuse_mount::FuseMountShared;
use crate::utils::fuse::fuse_sys::FUSE_ALIGNMENT;
use crate::utils::fuse::fuse_sys::FUSE_MIN_READ_BUFFER;
use crate::utils::fuse::fuse_sys::FUSE_ROOT_ID;
use crate::utils::line_logger::log_lines;
use crate::utils::pipe::Pipe;
use crate::utils::pipe::pipe;
use bstr::ByteSlice;
use jay_algorithms::oserror::OsErrorExt2;
use std::collections::VecDeque;
use std::convert::Infallible;
use std::future::pending;
use std::mem::ManuallyDrop;
use std::rc::Rc;
use std::rc::Weak;
use std::slice;
use uapi::c;

impl FuseMgr {
    pub(super) fn fusermount(
        &self,
        forker: Option<Rc<ForkerProxy>>,
        shared: &Rc<FuseMountEarlyShared>,
        path: &str,
        owner: Rc<FuseMountOwnerHolder>,
        root: Weak<dyn FuseInode>,
    ) -> SpawnedFuture<()> {
        let fut = fusermount(
            shared.clone(),
            self.eng.clone(),
            self.ring.clone(),
            forker,
            path.to_string(),
            owner,
            root,
        );
        self.eng.spawn("fusermount", fut)
    }
}

const FUSE_COMMFD_ENV: &str = "_FUSE_COMMFD";
const FUSERMOUNT3: &str = "fusermount3";

async fn fusermount(
    shared: Rc<FuseMountEarlyShared>,
    eng: Rc<AsyncEngine>,
    ring: Rc<IoUring>,
    forker: Option<Rc<ForkerProxy>>,
    path: String,
    holder: Rc<FuseMountOwnerHolder>,
    root: Weak<dyn FuseInode>,
) {
    let res = try_fusermount(&shared, &eng, &ring, forker, &path, &holder, root).await;
    holder.error.set(Some(res.unwrap_err()));
}

async fn try_fusermount(
    shared: &Rc<FuseMountEarlyShared>,
    eng: &Rc<AsyncEngine>,
    ring: &Rc<IoUring>,
    forker: Option<Rc<ForkerProxy>>,
    path: &str,
    holder: &Rc<FuseMountOwnerHolder>,
    root: Weak<dyn FuseInode>,
) -> Result<Infallible, FuseError> {
    let inodes = InodeCache::new()?;
    let Some(root) = root.upgrade() else {
        return Err(FuseError::RootInodeDropped);
    };
    let root = inodes.lookup(None, 0, root, 0);
    if root.ino.0.get() != FUSE_ROOT_ID {
        return Err(FuseError::RootInodeId);
    }
    if root.ty != FuseInodeTy::Directory {
        return Err(FuseError::RootInodeDir);
    }
    let Some(forker) = forker else {
        return Err(FuseError::NoForker);
    };
    let (p, c) = uapi::socketpair(c::AF_UNIX, c::SOCK_STREAM | c::SOCK_CLOEXEC, 0)
        .map_os_err(FuseError::CreateSocketpair)?;
    let Pipe { read, write } = pipe()
        .map_err(FuseError::CreatePipe)?
        .map_read(Rc::new)
        .map_write(Rc::new);
    let log_stderr = {
        let ring = ring.clone();
        async move {
            let res = log_lines(&ring, &read, |a, b| {
                log::info!("fusermount3: {}{}", a.as_bstr(), b.as_bstr());
            })
            .await;
            if let Err(e) = res {
                log::error!("Could not read from fusermount3 pipe: {}", ErrorFmt(e));
            }
        }
    };
    let _log_stderr = eng.spawn("fusermount3 stderr", log_stderr);
    let args = vec![
        "-o".to_string(),
        "rw,noatime,auto_unmount,default_permissions,fsname=jay-fuse".to_string(),
        path.to_string(),
    ];
    let env = vec![(FUSE_COMMFD_ENV.to_string(), Some("3".to_string()))];
    let fds = vec![(2, write), (3, Rc::new(c))];
    forker.spawn(FUSERMOUNT3.to_string(), args, env, fds);
    let mut buf = Buf::new(1);
    let p = Rc::new(p);
    let mut fds = VecDeque::new();
    ring.recvmsg(&p, slice::from_mut(&mut buf), &mut fds)
        .await
        .map_err(FuseError::Recvmsg)?;
    let fd = fds.pop_front().ok_or(FuseError::NoFd)?;
    let buffer_ring = ring
        .create_buffer_ring(16, FUSE_MIN_READ_BUFFER, FUSE_ALIGNMENT)
        .map_err(FuseError::CreateBufferRing)?;
    let shared = Rc::new(FuseMountShared {
        eng: eng.clone(),
        ring: ring.clone(),
        _socket: p,
        fd,
        buffer_ring,
        cache: Default::default(),
        inodes: ManuallyDrop::new(inodes),
        early: shared.clone(),
        fh: Default::default(),
        dirs: Default::default(),
        dirents: Default::default(),
        files: Default::default(),
        contents: Default::default(),
    });
    shared
        .schedule_read_requests()
        .map_err(FuseError::ScheduleReadRequest)?;
    let _prune = eng.spawn("fuse-prune", shared.clone().periodic_prune());
    holder.success();
    let _ = ring.poll(&shared.fd, 0).await;
    shared.fatal(FuseError::Aborted);
    pending().await
}