jay-compositor 1.15.0

The Jay compositor
use crate::utils::box_cache::BoxReset;
use crate::utils::box_cache::CachedBox;
use crate::utils::fuse::fuse_inode::FuseInode;
use crate::utils::fuse::fuse_inode::FuseInodePropsPti;
use crate::utils::fuse::fuse_inode::FuseInodeTy;
use crate::utils::fuse::fuse_inode::FuseInodeWithKey;
use crate::utils::fuse::fuse_inode_cache::InodeCache;
use crate::utils::fuse::fuse_inode_cache::fuse_inode_cache_types::ParentKeyRef;
use crate::utils::fuse::fuse_mgr::FuseIno;
use crate::utils::fuse::fuse_sys::fuse_dirent;
use crate::utils::fuse::fuse_sys::fuse_direntplus;
use crate::utils::fuse::fuse_sys::fuse_direntplus_size;
use crate::utils::fuse::fuse_sys::fuse_entry_out;
use jay_proc::Reset;
use std::ops::Range;
use std::rc::Rc;
use std::rc::Weak;
use uapi::c;
use uapi::pod_zeroed;

mod fuse_snapshot;

pub const FUSE_NO_TIMEOUT: u64 = u64::MAX;
pub const FUSE_SHORT_TIMEOUT: u64 = 100_000_000;

pub struct FuseDirent {
    pub inode: Rc<dyn FuseInode>,
    pub key: u64,
    pub static_name: Option<&'static str>,
    pub timeout_ns: u64,
}

pub(super) struct FuseOpenDir {
    pub(super) ino: FuseIno,
    pub(super) inode: Weak<dyn FuseInode>,
    pub(super) key: u64,
    pub(super) depth: u64,
    pub(super) dirents: CachedBox<FuseDirents, BoxReset>,
    pub(super) have_dirents: bool,
}

#[derive(Default, Reset)]
pub struct FuseDirents {
    dirents: Vec<Dirent>,
    names: String,
}

struct Dirent {
    inode: Option<DirentInode>,
    name: DirentName,
}

enum DirentName {
    Static(&'static str),
    Dynamic(Range<usize>),
}

struct DirentInode {
    inode: Weak<dyn FuseInode>,
    props: FuseInodePropsPti,
    key: u64,
    timeout_ns: u64,
}

pub enum FuseDirentName<'a> {
    Static(&'static str),
    Dynamic(&'a str),
}

impl<'a> From<&'a str> for FuseDirentName<'a> {
    fn from(value: &'a str) -> Self {
        Self::Dynamic(value)
    }
}

impl DirentName {
    fn get<'a>(&self, ents: &'a FuseDirents) -> &'a str {
        match self {
            DirentName::Static(n) => n,
            DirentName::Dynamic(r) => &ents.names[r.clone()],
        }
    }

    fn static_name(&self) -> Option<&'static str> {
        match self {
            DirentName::Static(n) => Some(*n),
            DirentName::Dynamic(_) => None,
        }
    }
}

impl FuseDirents {
    pub fn add<I>(&mut self, timeout_ns: u64, inode: &Rc<I>, key: u64, name: FuseDirentName<'_>)
    where
        I: FuseInode + 'static,
    {
        let inode = DirentInode {
            props: inode.props_pti(key),
            inode: inode.downgrade(),
            key,
            timeout_ns,
        };
        self.add_(Some(inode), name);
    }

    pub fn add_dyn(
        &mut self,
        timeout_ns: u64,
        FuseInodeWithKey { inode, key }: FuseInodeWithKey,
        name: FuseDirentName<'_>,
    ) {
        let inode = DirentInode {
            props: inode.props_pti(key),
            inode: Rc::downgrade(&inode),
            key,
            timeout_ns,
        };
        self.add_(Some(inode), name);
    }

    fn add_(&mut self, inode: Option<DirentInode>, name: FuseDirentName<'_>) {
        let name = match name {
            FuseDirentName::Static(n) => DirentName::Static(n),
            FuseDirentName::Dynamic(n) => {
                let lo = self.names.len();
                self.names.push_str(n);
                let hi = self.names.len();
                DirentName::Dynamic(lo..hi)
            }
        };
        self.dirents.push(Dirent { inode, name });
    }
}

impl Dirent {
    fn dirent(&self, name: &str, idx: usize, ino: Option<FuseIno>) -> fuse_dirent {
        let mut ty = c::DT_DIR;
        if let Some(inode) = &self.inode {
            ty = match inode.props.ty {
                FuseInodeTy::Regular => c::DT_REG,
                FuseInodeTy::Directory => c::DT_DIR,
                FuseInodeTy::Symlink => c::DT_LNK,
            };
        }
        fuse_dirent {
            ino: ino.map(|v| v.0.get()).unwrap_or(0),
            off: (idx + 1) as u64,
            namelen: name.len() as _,
            type_: ty as _,
            name: [],
        }
    }

    fn direntplus(&self, name: &str, idx: usize, ino: Option<FuseIno>) -> fuse_direntplus {
        let mut attr = pod_zeroed();
        let mut entry_valid = 0;
        let mut entry_valid_nsec = 0;
        if let Some(inode) = &self.inode {
            attr = inode.props.props.attr(ino);
            entry_valid = inode.timeout_ns / 1_000_000_000;
            entry_valid_nsec = (inode.timeout_ns % 1_000_000_000) as u32;
        }
        let dirent = self.dirent(name, idx, ino);
        fuse_direntplus {
            entry_out: fuse_entry_out {
                nodeid: dirent.ino,
                attr,
                attr_valid: u64::MAX,
                entry_valid,
                entry_valid_nsec,
                ..pod_zeroed()
            },
            dirent,
        }
    }
}

impl FuseOpenDir {
    fn ensure(&mut self) {
        if self.have_dirents {
            return;
        }
        self.have_dirents = true;
        self.dirents.add_(None, FuseDirentName::Static("."));
        self.dirents.add_(None, FuseDirentName::Static(".."));
        if let Some(v) = self.inode.upgrade() {
            v.getdents(self.key, &mut self.dirents);
        }
    }

    pub(super) fn encode_plus(
        &mut self,
        inodes: &InodeCache,
        offset: usize,
        size: usize,
        dst: &mut Vec<u8>,
    ) {
        self.ensure();
        let dirents = &self.dirents;
        for (idx, ent) in dirents.dirents.iter().enumerate().skip(offset) {
            let name = ent.name.get(dirents);
            let len = dst.len() + fuse_direntplus_size(name.len());
            if len > size {
                break;
            }
            let mut ino = None;
            if let Some(dirent) = &ent.inode {
                let parent = ParentKeyRef::new(self.ino, ent.name.static_name(), name);
                ino = Some(
                    inodes
                        .lookup2(
                            Some(parent),
                            self.depth,
                            dirent.inode.as_ptr().addr(),
                            || dirent.inode.clone(),
                            dirent.key,
                            dirent.props,
                        )
                        .ino,
                );
            }
            let plus = ent.direntplus(name, idx, ino);
            dst.extend_from_slice(uapi::as_bytes(&plus));
            dst.extend_from_slice(name.as_bytes());
            dst.resize(len, 0);
        }
    }
}