use super::config::OverlayMountData;
use super::entry::OvlLayer;
use super::inode::{DirState, OvlInode, OvlLinkState};
use crate::driver::base::device::device_number::DeviceNumber;
use crate::filesystem::vfs::mount::{MountFS, MountFSInode};
use crate::filesystem::vfs::{
    self, vcore::generate_inode_id, FileSystem, FileSystemMakerData, FileType, FsInfo, IndexNode,
    InodeId, LinkMutationCoordinator, MountableFileSystem, SuperBlock,
};
use crate::libs::{casting::DowncastArc, mutex::Mutex};
use crate::process::Cred;
use crate::process::ProcessManager;
use alloc::collections::{BTreeMap, VecDeque};
use alloc::string::String;
use alloc::sync::{Arc, Weak};
use alloc::vec::Vec;
use core::sync::atomic::{AtomicBool, Ordering};
use hashbrown::HashMap;
use system_error::SystemError;

const MAX_MOUNT_ANCESTOR_DEPTH: usize = vfs::MAX_PATHLEN;
const INODE_CACHE_PRUNE_INTERVAL: usize = 256;
type LowerRoot = (String, Arc<dyn IndexNode>);

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(super) struct RealInodeIdentity {
    filesystem: usize,
    dev_id: usize,
    inode_id: InodeId,
}

impl RealInodeIdentity {
    pub(super) fn from_inode(inode: &Arc<dyn IndexNode>) -> Result<Self, SystemError> {
        let fs = OverlayFS::canonical_backing_fs(inode);
        let metadata = inode.metadata()?;
        Ok(Self {
            filesystem: Arc::as_ptr(&fs) as *const () as usize,
            dev_id: metadata.dev_id,
            inode_id: metadata.inode_id,
        })
    }
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum OvlInodeOrigin {
    Lower {
        lower: Vec<RealInodeIdentity>,
        upper: Option<RealInodeIdentity>,
    },
    Upper(RealInodeIdentity),
}

/// Stable identity of one overlay-visible non-directory inode.
///
/// Once an inode has a lower origin, copy-up must not change this identity.
/// Pure-upper inodes use the backing upper identity directly.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(super) enum OvlLinkIdentity {
    Lower(RealInodeIdentity),
    Upper(RealInodeIdentity),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct OvlLinkSeed {
    identity: OvlLinkIdentity,
    initial_nlinks: usize,
}

impl OvlLinkSeed {
    pub(super) fn new(identity: OvlLinkIdentity, initial_nlinks: usize) -> Self {
        Self {
            identity,
            initial_nlinks,
        }
    }

    pub(super) fn identity(self) -> OvlLinkIdentity {
        self.identity
    }

    pub(super) fn initial_nlinks(self) -> usize {
        self.initial_nlinks
    }
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct OvlInodeCacheKey {
    redirect: String,
    origin: OvlInodeOrigin,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum DirIdentity {
    Lower(Vec<RealInodeIdentity>),
    Upper(RealInodeIdentity),
}

#[derive(Debug, Default)]
struct DirStateCache {
    entries: BTreeMap<DirIdentity, Weak<DirState>>,
    insertions_since_prune: usize,
}

impl DirStateCache {
    fn intern(&mut self, key: DirIdentity) -> Arc<DirState> {
        if let Some(state) = self.entries.get(&key).and_then(Weak::upgrade) {
            return state;
        }
        if self.insertions_since_prune >= INODE_CACHE_PRUNE_INTERVAL {
            self.entries.retain(|_, state| state.strong_count() != 0);
            self.insertions_since_prune = 0;
        }
        let state = Arc::new(DirState::default());
        self.entries.insert(key, Arc::downgrade(&state));
        self.insertions_since_prune += 1;
        state
    }
}

#[derive(Debug, Default)]
struct OvlInodeCache {
    entries: BTreeMap<OvlInodeCacheKey, Weak<OvlInode>>,
    /// Mount-global bounded retention for repeated short-lived lookups.
    recent_lookups: VecDeque<Arc<OvlInode>>,
    /// Bounded strong cache for non-samefs directories whose fallback inode
    /// number is only meaningful for the OvlInode cache lifetime.
    recent_fallback_dirs: VecDeque<Arc<OvlInode>>,
    insertions_since_prune: usize,
}

#[derive(Debug, Default)]
struct OvlLinkStateCache {
    entries: HashMap<OvlLinkIdentity, Weak<OvlLinkState>>,
    coordinators: HashMap<OvlLinkIdentity, Weak<LinkMutationCoordinator>>,
    /// Copy-up publishes this small identity connector before the new upper
    /// name becomes visible. It preserves a lower-origin seed without eagerly
    /// allocating the shared mutation state on lookup or open.
    connectors: HashMap<RealInodeIdentity, OvlLinkSeed>,
    /// Lower-origin states are created only for a logical link-count mutation.
    /// A mutation guard pins here before the backing commit so retaining the
    /// authoritative union count can never allocate after namespace change.
    divergent: HashMap<OvlLinkIdentity, Arc<OvlLinkState>>,
    insertions_since_prune: usize,
    coordinator_insertions_since_prune: usize,
}

impl OvlLinkStateCache {
    fn intern_coordinator(
        &mut self,
        identity: OvlLinkIdentity,
    ) -> Result<Arc<LinkMutationCoordinator>, SystemError> {
        if let Some(coordinator) = self.coordinators.get(&identity).and_then(Weak::upgrade) {
            return Ok(coordinator);
        }
        if self.coordinator_insertions_since_prune >= INODE_CACHE_PRUNE_INTERVAL {
            self.coordinators
                .retain(|_, coordinator| coordinator.strong_count() != 0);
            self.coordinator_insertions_since_prune = 0;
        }
        self.coordinators
            .try_reserve(1)
            .map_err(|_| SystemError::ENOMEM)?;
        let coordinator =
            Arc::try_new(LinkMutationCoordinator::new()).map_err(|_| SystemError::ENOMEM)?;
        self.coordinators
            .insert(identity, Arc::downgrade(&coordinator));
        self.coordinator_insertions_since_prune += 1;
        Ok(coordinator)
    }

    fn resolve(&self, identity: OvlLinkIdentity) -> Option<Arc<OvlLinkState>> {
        self.entries.get(&identity).and_then(Weak::upgrade)
    }

    fn intern(
        &mut self,
        identity: OvlLinkIdentity,
        upper_alias: Option<OvlLinkIdentity>,
        nlinks: usize,
    ) -> Result<Arc<OvlLinkState>, SystemError> {
        if let Some(state) = self
            .resolve(identity)
            .or_else(|| upper_alias.and_then(|upper_identity| self.resolve(upper_identity)))
        {
            self.entries
                .try_reserve(1 + usize::from(upper_alias.is_some()))
                .map_err(|_| SystemError::ENOMEM)?;
            self.entries.insert(identity, Arc::downgrade(&state));
            if let Some(upper_identity) = upper_alias {
                self.entries.insert(upper_identity, Arc::downgrade(&state));
            }
            return Ok(state);
        }
        if self.insertions_since_prune >= INODE_CACHE_PRUNE_INTERVAL {
            self.entries.retain(|_, state| state.strong_count() != 0);
            self.insertions_since_prune = 0;
        }
        self.entries
            .try_reserve(1 + usize::from(upper_alias.is_some()))
            .map_err(|_| SystemError::ENOMEM)?;
        let state =
            Arc::try_new(OvlLinkState::new(identity, nlinks)).map_err(|_| SystemError::ENOMEM)?;
        self.entries.insert(identity, Arc::downgrade(&state));
        if let Some(upper_identity) = upper_alias {
            self.entries.insert(upper_identity, Arc::downgrade(&state));
        }
        self.insertions_since_prune += 1;
        Ok(state)
    }

    fn begin_mutation(&mut self, state: &Arc<OvlLinkState>) -> Result<bool, SystemError> {
        let identity = state.identity();
        if self.divergent.contains_key(&identity) {
            return Ok(false);
        }
        self.divergent
            .try_reserve(1)
            .map_err(|_| SystemError::ENOMEM)?;
        self.divergent.insert(identity, state.clone());
        Ok(true)
    }

    fn abort_mutation(&mut self, state: &Arc<OvlLinkState>, newly_pinned: bool) {
        if newly_pinned
            && self
                .divergent
                .get(&state.identity())
                .is_some_and(|current| Arc::ptr_eq(current, state))
        {
            self.divergent.remove(&state.identity());
        }
    }

    fn alias(
        &mut self,
        alias: OvlLinkIdentity,
        state: &Arc<OvlLinkState>,
    ) -> Result<(), SystemError> {
        self.entries
            .try_reserve(1)
            .map_err(|_| SystemError::ENOMEM)?;
        self.entries.insert(alias, Arc::downgrade(state));
        Ok(())
    }

    fn commit_mutation(&mut self, state: &Arc<OvlLinkState>, nlinks: usize) {
        let identity = state.identity();
        if nlinks == 0 {
            if self
                .divergent
                .get(&identity)
                .is_some_and(|current| Arc::ptr_eq(current, state))
            {
                self.divergent.remove(&identity);
            }
            self.connectors
                .retain(|_, seed| seed.identity() != identity);
            self.entries.retain(|_, cached| {
                cached
                    .upgrade()
                    .is_some_and(|current| !Arc::ptr_eq(&current, state))
            });
        } else {
            debug_assert!(self.divergent.contains_key(&identity));
        }
    }

    fn connector(&self, upper: RealInodeIdentity) -> Option<OvlLinkSeed> {
        self.connectors.get(&upper).copied()
    }
}

pub(super) struct OvlLinkSeedPublication {
    fs: Arc<OverlayFS>,
    upper: RealInodeIdentity,
    seed: OvlLinkSeed,
    inserted: bool,
    committed: bool,
}

impl OvlLinkSeedPublication {
    pub(super) fn commit(mut self) {
        self.committed = true;
    }
}

impl Drop for OvlLinkSeedPublication {
    fn drop(&mut self) {
        if self.committed || !self.inserted {
            return;
        }
        let mut cache = self.fs.link_state_cache.lock();
        if cache.connectors.get(&self.upper) == Some(&self.seed) {
            cache.connectors.remove(&self.upper);
        }
        if cache.connectors.is_empty() {
            self.fs
                .link_seed_connectors_present
                .store(false, Ordering::Release);
        }
    }
}

#[derive(Debug)]
pub(super) struct AncestorPublication {
    lower: Vec<RealInodeIdentity>,
    upper: Weak<dyn IndexNode>,
}

#[derive(Debug, Default)]
struct AncestorPublicationCache {
    entries: BTreeMap<String, AncestorPublication>,
    insertions_since_prune: usize,
}

const FALLBACK_DIR_CACHE_SIZE: usize = 256;
const LOOKUP_RETENTION_SIZE: usize = 1024;
const LOCK_STRIPE_COUNT: usize = 64;

impl OvlInodeCache {
    fn lookup(&self, key: &OvlInodeCacheKey) -> Option<Arc<OvlInode>> {
        self.entries.get(key).and_then(Weak::upgrade)
    }

    fn still_contains(&self, key: &OvlInodeCacheKey, expected: Option<&Arc<OvlInode>>) -> bool {
        match (self.lookup(key), expected) {
            (Some(current), Some(expected)) => Arc::ptr_eq(&current, expected),
            (None, None) => true,
            _ => false,
        }
    }

    fn retain(&mut self, inode: Arc<OvlInode>, retain_fallback_dir: bool) {
        self.recent_lookups
            .retain(|cached| !Arc::ptr_eq(cached, &inode));
        self.recent_lookups.push_back(inode.clone());
        if self.recent_lookups.len() > LOOKUP_RETENTION_SIZE {
            self.recent_lookups.pop_front();
        }

        if retain_fallback_dir {
            self.recent_fallback_dirs
                .retain(|cached| !Arc::ptr_eq(cached, &inode));
            self.recent_fallback_dirs.push_back(inode.clone());
            if self.recent_fallback_dirs.len() > FALLBACK_DIR_CACHE_SIZE {
                self.recent_fallback_dirs.pop_front();
            }
        }
    }

    fn replace(&mut self, key: OvlInodeCacheKey, inode: Arc<OvlInode>, retain_fallback_dir: bool) {
        if self.insertions_since_prune >= INODE_CACHE_PRUNE_INTERVAL {
            self.entries.retain(|_, inode| inode.strong_count() != 0);
            self.insertions_since_prune = 0;
        }
        self.entries.insert(key, Arc::downgrade(&inode));
        self.insertions_since_prune += 1;
        self.retain(inode, retain_fallback_dir);
    }
}

#[derive(Debug)]
#[allow(dead_code)]
pub(super) struct OvlSuperBlock {
    super_block: SuperBlock,
    pseudo_dev: DeviceNumber, // virtual device number
    is_lower: bool,
}

#[derive(Debug)]
pub(super) struct OverlayFS {
    #[allow(dead_code)]
    pub(super) numlayer: usize,
    #[allow(dead_code)]
    pub(super) numfs: u32,
    #[allow(dead_code)]
    pub(super) numdatalayer: usize,
    pub(super) layers: Vec<OvlLayer>, // layer 0 is read-write, subsequent layers are read-only
    pub(super) workdir: Arc<dyn IndexNode>,
    pub(super) root_inode: Arc<OvlInode>,
    pub(super) super_block: SuperBlock,
    pub(super) mutation_lock: Mutex<()>,
    pub(super) backing_cred: Arc<Cred>,
    pub(super) samefs: bool,
    inode_cache: Mutex<OvlInodeCache>,
    link_state_cache: Mutex<OvlLinkStateCache>,
    link_seed_connectors_present: AtomicBool,
    dir_state_cache: Mutex<DirStateCache>,
    ancestor_publications: Mutex<AncestorPublicationCache>,
    copy_up_locks: Vec<Mutex<()>>,
    ancestor_copy_up_locks: Vec<Mutex<()>>,
    content_locks: Vec<Mutex<()>>,
}

impl FileSystem for OverlayFS {
    fn page_cache_writeback_domain(
        &self,
    ) -> Option<&Arc<crate::filesystem::page_cache::PageCacheWritebackDomain>> {
        None
    }

    fn root_inode(&self) -> Arc<dyn IndexNode> {
        self.root_inode.clone()
    }

    fn info(&self) -> vfs::FsInfo {
        FsInfo {
            blk_dev_id: 0,
            max_name_len: 255,
        }
    }

    fn as_any_ref(&self) -> &dyn core::any::Any {
        self
    }

    fn name(&self) -> &str {
        "overlayfs"
    }

    fn super_block(&self) -> SuperBlock {
        self.super_block.clone()
    }
}

impl OverlayFS {
    pub(super) fn ovl_upper_mnt(&self) -> Arc<OvlInode> {
        self.layers[0].mnt.clone()
    }

    pub(super) fn intern_inode(
        self: &Arc<Self>,
        redirect: String,
        file_type: FileType,
        upper_inode: Option<Arc<dyn IndexNode>>,
        lower_inodes: Vec<Arc<dyn IndexNode>>,
        replace_stale: bool,
        initial_nlinks: usize,
    ) -> Result<Option<Arc<OvlInode>>, SystemError> {
        let upper_identity = upper_inode
            .as_ref()
            .map(RealInodeIdentity::from_inode)
            .transpose()?;
        let recovered_seed = if lower_inodes.is_empty() && file_type != FileType::Dir {
            upper_identity.and_then(|upper| self.resolve_link_seed(upper))
        } else {
            None
        };
        let origin = if let Some(seed) = recovered_seed {
            let OvlLinkIdentity::Lower(lower) = seed.identity() else {
                return Err(SystemError::EIO);
            };
            OvlInodeOrigin::Lower {
                lower: alloc::vec![lower],
                upper: upper_identity,
            }
        } else if lower_inodes.is_empty() {
            OvlInodeOrigin::Upper(upper_identity.ok_or(SystemError::ENOENT)?)
        } else {
            OvlInodeOrigin::Lower {
                lower: lower_inodes
                    .iter()
                    .map(RealInodeIdentity::from_inode)
                    .collect::<Result<Vec<_>, _>>()?,
                upper: upper_identity,
            }
        };
        let link_identity = match &origin {
            OvlInodeOrigin::Lower { lower, .. } => {
                lower.first().copied().map(OvlLinkIdentity::Lower)
            }
            OvlInodeOrigin::Upper(identity) => Some(OvlLinkIdentity::Upper(*identity)),
        };
        let key = OvlInodeCacheKey {
            redirect: redirect.clone(),
            origin,
        };
        let retain_fallback_dir = !self.samefs && file_type == FileType::Dir;
        let expected_has_upper = upper_inode.is_some();
        let inode = loop {
            // Never inspect an inode while holding inode_cache: copy-up owns
            // upper_inode before consulting this cache for ancestor objects.
            let candidate = self.inode_cache.lock().lookup(&key);
            if let Some(candidate) = candidate.as_ref() {
                if candidate.has_upper() == expected_has_upper {
                    let mut cache = self.inode_cache.lock();
                    if cache.still_contains(&key, Some(candidate)) {
                        cache.retain(candidate.clone(), retain_fallback_dir);
                        break candidate.clone();
                    }
                    continue;
                }
                // A lower-only backing snapshot can race with this candidate's
                // copy-up publication.  Ask lookup to refresh the backing once
                // before deciding that the cached upper is detached.
                if !expected_has_upper && !replace_stale {
                    return Ok(None);
                }
            }

            let replacement = {
                let fallback_id = retain_fallback_dir.then(generate_inode_id);
                // A cache hit already owns the canonical coordinator. Only
                // intern one when this lookup really needs a new inode; the
                // cache comparison below decides which racing replacement is
                // published, and the loser drops its temporary references.
                let link_coordinator = match link_identity {
                    Some(identity) if file_type != FileType::Dir => {
                        self.link_state_cache.lock().intern_coordinator(identity)?
                    }
                    _ => Arc::try_new(LinkMutationCoordinator::new())
                        .map_err(|_| SystemError::ENOMEM)?,
                };
                let inode = Arc::new(OvlInode::new(
                    redirect.clone(),
                    file_type,
                    upper_inode.clone(),
                    lower_inodes.clone(),
                    fallback_id,
                    link_coordinator,
                ));
                inode.set_fs(Arc::downgrade(self));
                inode
            };
            let mut cache = self.inode_cache.lock();
            if cache.still_contains(&key, candidate.as_ref()) {
                cache.replace(key.clone(), replacement.clone(), retain_fallback_dir);
                break replacement;
            }
        };
        inode.load_origin_once()?;
        if let Some(identity) = link_identity {
            inode.install_link_seed(
                identity,
                recovered_seed
                    .map(OvlLinkSeed::initial_nlinks)
                    .unwrap_or(initial_nlinks),
            );
        }
        Ok(Some(inode))
    }

    pub(super) fn resolve_link_state(
        &self,
        identity: OvlLinkIdentity,
    ) -> Option<Arc<OvlLinkState>> {
        self.link_state_cache.lock().resolve(identity)
    }

    pub(super) fn intern_link_state(
        &self,
        identity: OvlLinkIdentity,
        upper_alias: Option<OvlLinkIdentity>,
        initial_nlinks: usize,
    ) -> Result<Arc<OvlLinkState>, SystemError> {
        self.link_state_cache
            .lock()
            .intern(identity, upper_alias, initial_nlinks)
    }

    pub(super) fn begin_link_mutation(
        &self,
        state: &Arc<OvlLinkState>,
    ) -> Result<bool, SystemError> {
        self.link_state_cache.lock().begin_mutation(state)
    }

    pub(super) fn abort_link_mutation(&self, state: &Arc<OvlLinkState>, newly_pinned: bool) {
        self.link_state_cache
            .lock()
            .abort_mutation(state, newly_pinned);
    }

    pub(super) fn alias_link_state(
        &self,
        alias: OvlLinkIdentity,
        state: &Arc<OvlLinkState>,
    ) -> Result<(), SystemError> {
        self.link_state_cache.lock().alias(alias, state)
    }

    pub(super) fn commit_link_mutation(&self, state: &Arc<OvlLinkState>, nlinks: usize) {
        let mut cache = self.link_state_cache.lock();
        cache.commit_mutation(state, nlinks);
        if cache.connectors.is_empty() {
            self.link_seed_connectors_present
                .store(false, Ordering::Release);
        }
    }

    fn resolve_link_seed(&self, upper: RealInodeIdentity) -> Option<OvlLinkSeed> {
        if !self.link_seed_connectors_present.load(Ordering::Acquire) {
            return None;
        }
        self.link_state_cache.lock().connector(upper)
    }

    pub(super) fn prepare_link_seed_publication(
        self: &Arc<Self>,
        upper: RealInodeIdentity,
        seed: OvlLinkSeed,
    ) -> Result<OvlLinkSeedPublication, SystemError> {
        let mut cache = self.link_state_cache.lock();
        let inserted = match cache.connectors.get(&upper) {
            Some(current) if *current == seed => false,
            Some(_) => return Err(SystemError::EIO),
            None => {
                cache
                    .connectors
                    .try_reserve(1)
                    .map_err(|_| SystemError::ENOMEM)?;
                cache.connectors.insert(upper, seed);
                true
            }
        };
        self.link_seed_connectors_present
            .store(true, Ordering::Release);
        drop(cache);
        Ok(OvlLinkSeedPublication {
            fs: self.clone(),
            upper,
            seed,
            inserted,
            committed: false,
        })
    }

    pub(super) fn cached_lower_dirs(
        &self,
        redirect: &str,
        lowers: &[Arc<dyn IndexNode>],
    ) -> Result<Vec<Arc<OvlInode>>, SystemError> {
        let lower_identities = lowers
            .iter()
            .map(RealInodeIdentity::from_inode)
            .collect::<Result<Vec<_>, _>>()?;
        let cache = self.inode_cache.lock();
        let mut inodes = cache
            .entries
            .iter()
            .filter_map(|(key, inode)| {
                let OvlInodeOrigin::Lower { lower, .. } = &key.origin else {
                    return None;
                };
                (key.redirect == redirect && *lower == lower_identities)
                    .then(|| inode.upgrade())
                    .flatten()
            })
            .filter(|inode| inode.is_dir())
            .collect::<Vec<_>>();
        inodes.sort_by_key(Arc::as_ptr);
        inodes.dedup_by(|left, right| Arc::ptr_eq(left, right));
        Ok(inodes)
    }

    pub(super) fn prepare_ancestor_publication(
        &self,
        lowers: &[Arc<dyn IndexNode>],
        upper: &Arc<dyn IndexNode>,
    ) -> Result<AncestorPublication, SystemError> {
        let lower = lowers
            .iter()
            .map(RealInodeIdentity::from_inode)
            .collect::<Result<Vec<_>, _>>()?;
        Ok(AncestorPublication {
            lower,
            upper: Arc::downgrade(upper),
        })
    }

    pub(super) fn remember_ancestor_publication(
        &self,
        redirect: &str,
        publication: AncestorPublication,
    ) {
        let mut cache = self.ancestor_publications.lock();
        if cache.insertions_since_prune >= INODE_CACHE_PRUNE_INTERVAL {
            cache
                .entries
                .retain(|_, publication| publication.upper.strong_count() != 0);
            cache.insertions_since_prune = 0;
        }
        cache.entries.insert(String::from(redirect), publication);
        cache.insertions_since_prune += 1;
    }

    pub(super) fn matches_ancestor_publication(
        &self,
        redirect: &str,
        lowers: &[Arc<dyn IndexNode>],
        upper: &Arc<dyn IndexNode>,
    ) -> Result<bool, SystemError> {
        let lower = lowers
            .iter()
            .map(RealInodeIdentity::from_inode)
            .collect::<Result<Vec<_>, _>>()?;
        let published = self
            .ancestor_publications
            .lock()
            .entries
            .get(redirect)
            .and_then(|publication| {
                (publication.lower == lower)
                    .then(|| publication.upper.upgrade())
                    .flatten()
            });
        Ok(published.is_some_and(|published| Arc::ptr_eq(&published, upper)))
    }

    pub(super) fn same_backing_inode(
        &self,
        left: &Arc<dyn IndexNode>,
        right: &Arc<dyn IndexNode>,
    ) -> Result<bool, SystemError> {
        Self::same_mount_inode(left, right)
    }

    pub(super) fn intern_dir_state(&self, inode: &OvlInode) -> Result<Arc<DirState>, SystemError> {
        let identity = if inode.lower_inodes.is_empty() {
            DirIdentity::Upper(RealInodeIdentity::from_inode(
                inode
                    .upper_inode
                    .lock()
                    .as_ref()
                    .ok_or(SystemError::ENOENT)?,
            )?)
        } else {
            DirIdentity::Lower(
                inode
                    .lower_inodes
                    .iter()
                    .map(RealInodeIdentity::from_inode)
                    .collect::<Result<Vec<_>, _>>()?,
            )
        };
        Ok(self.dir_state_cache.lock().intern(identity))
    }

    fn redirect_lock_index(&self, redirect: &str) -> usize {
        let mut hash = 0xcbf29ce484222325u64;
        for byte in redirect.as_bytes() {
            hash ^= *byte as u64;
            hash = hash.wrapping_mul(0x100000001b3);
        }
        hash as usize % LOCK_STRIPE_COUNT
    }

    pub(super) fn copy_up_lock(&self, redirect: &str) -> &Mutex<()> {
        &self.copy_up_locks[self.redirect_lock_index(redirect)]
    }

    pub(super) fn ancestor_copy_up_lock(&self, redirect: &str) -> &Mutex<()> {
        &self.ancestor_copy_up_locks[self.redirect_lock_index(redirect)]
    }

    pub(super) fn content_lock(
        &self,
        inode: &Arc<dyn IndexNode>,
    ) -> Result<&Mutex<()>, SystemError> {
        let identity = RealInodeIdentity::from_inode(inode)?;
        let mut hash = identity.filesystem as u64;
        hash ^= (identity.dev_id as u64).rotate_left(21);
        hash ^= (identity.inode_id.data() as u64).rotate_left(42);
        Ok(&self.content_locks[hash as usize % self.content_locks.len()])
    }

    fn canonical_backing_fs(inode: &Arc<dyn IndexNode>) -> Arc<dyn FileSystem> {
        let mut fs = inode.fs();
        while let Some(mount_fs) = fs.clone().downcast_arc::<MountFS>() {
            fs = mount_fs.inner_filesystem();
        }
        fs
    }

    pub(super) fn backing_fsid(&self, inode: &Arc<dyn IndexNode>) -> Result<u32, SystemError> {
        let target_fs = Self::canonical_backing_fs(inode);
        // Origin records describe a lower object. Prefer a lower layer when the
        // upper and lower directories happen to share the same filesystem.
        for layer in self.layers.iter().skip(1).chain(self.layers.iter().take(1)) {
            let real = if layer.index == 0 {
                layer.mnt.upper_inode.lock().clone()
            } else {
                layer.mnt.lower_inodes.first().cloned()
            };
            let Some(real) = real else {
                continue;
            };
            if Arc::ptr_eq(&target_fs, &Self::canonical_backing_fs(&real)) {
                return Ok(layer.fsid);
            }
        }
        Err(SystemError::EXDEV)
    }

    pub(super) fn backing_fsid_matches_device(
        &self,
        fsid: u32,
        dev_id: usize,
    ) -> Result<bool, SystemError> {
        let Some(layer) = self.layers.iter().find(|layer| layer.fsid == fsid) else {
            return Ok(false);
        };
        let Some(lower) = layer.mnt.lower_inodes.first() else {
            return Ok(false);
        };
        Ok(lower.metadata()?.dev_id == dev_id)
    }

    fn same_mount_inode(
        left: &Arc<dyn IndexNode>,
        right: &Arc<dyn IndexNode>,
    ) -> Result<bool, SystemError> {
        let left = Self::canonical_inode(left.clone());
        let right = Self::canonical_inode(right.clone());
        if !Arc::ptr_eq(&left.fs(), &right.fs()) {
            return Ok(false);
        }

        Ok(left.metadata()?.inode_id == right.metadata()?.inode_id)
    }

    fn is_mount_ancestor(
        ancestor: &Arc<dyn IndexNode>,
        node: &Arc<dyn IndexNode>,
    ) -> Result<bool, SystemError> {
        let ancestor = Self::canonical_inode(ancestor.clone());
        let node = Self::canonical_inode(node.clone());
        if !Arc::ptr_eq(&ancestor.fs(), &node.fs()) {
            return Ok(false);
        }

        let mut current = node;
        for _ in 0..MAX_MOUNT_ANCESTOR_DEPTH {
            if Self::same_mount_inode(&ancestor, &current)? {
                return Ok(true);
            }

            let parent = Self::canonical_inode(current.parent().map_err(|_| SystemError::EINVAL)?);
            if Self::same_mount_inode(&parent, &current)? {
                return Ok(false);
            }
            if !Arc::ptr_eq(&ancestor.fs(), &parent.fs()) {
                return Ok(false);
            }
            current = parent;
        }

        Err(SystemError::ELOOP)
    }

    fn canonical_inode(mut inode: Arc<dyn IndexNode>) -> Arc<dyn IndexNode> {
        while let Some(mount_inode) = inode.clone().downcast_arc::<MountFSInode>() {
            inode = mount_inode.underlying_inode();
        }
        inode
    }

    fn layers_overlap_either_direction(
        left: &Arc<dyn IndexNode>,
        right: &Arc<dyn IndexNode>,
    ) -> Result<bool, SystemError> {
        Ok(Self::same_mount_inode(left, right)?
            || Self::is_mount_ancestor(left, right)?
            || Self::is_mount_ancestor(right, left)?)
    }
}

impl MountableFileSystem for OverlayFS {
    fn make_fs(
        data: Option<&dyn FileSystemMakerData>,
    ) -> Result<Arc<dyn FileSystem + 'static>, SystemError> {
        let mount_data = data
            .and_then(|d| d.as_any().downcast_ref::<OverlayMountData>())
            .ok_or(SystemError::EINVAL)?;
        let root_inode = ProcessManager::current_mntns().root_inode();
        let upper_inode = root_inode
            .lookup(&mount_data.upper_dir)
            .map_err(|_| SystemError::EINVAL)?;
        let upper_file_type = upper_inode.metadata()?.file_type;
        if upper_file_type != FileType::Dir {
            return Err(SystemError::EINVAL);
        }
        let upper_layer = OvlLayer {
            mnt: Arc::new(OvlInode::new(
                mount_data.upper_dir.clone(),
                upper_file_type,
                Some(upper_inode.clone()),
                Vec::new(),
                None,
                Arc::try_new(LinkMutationCoordinator::new()).map_err(|_| SystemError::ENOMEM)?,
            )),
            index: 0,
            fsid: 0,
        };

        let lower_roots: Result<Vec<LowerRoot>, SystemError> = mount_data
            .lower_dirs
            .iter()
            .map(|dir| {
                let lower_inode = ProcessManager::current_mntns()
                    .root_inode()
                    .lookup(dir)
                    .map_err(|_| SystemError::EINVAL)?;
                if lower_inode.metadata()?.file_type != FileType::Dir {
                    return Err(SystemError::EINVAL);
                }
                Ok((dir.clone(), lower_inode))
            })
            .collect();

        let lower_roots = lower_roots?;

        let lower_layers: Result<Vec<OvlLayer>, SystemError> = lower_roots
            .iter()
            .enumerate()
            .map(|(i, (dir, lower_inode))| {
                let lower_file_type = lower_inode.metadata()?.file_type;
                Ok(OvlLayer {
                    mnt: Arc::new(OvlInode::new(
                        dir.clone(),
                        lower_file_type,
                        None,
                        vec![lower_inode.clone()],
                        None,
                        Arc::try_new(LinkMutationCoordinator::new())
                            .map_err(|_| SystemError::ENOMEM)?,
                    )),
                    index: (i + 1) as u32,
                    fsid: (i + 1) as u32,
                })
            })
            .collect();

        let lower_layers = lower_layers?;

        let workdir_inode = root_inode
            .lookup(&mount_data.work_dir)
            .map_err(|_| SystemError::EINVAL)?;
        if workdir_inode.metadata()?.file_type != FileType::Dir {
            return Err(SystemError::EINVAL);
        }
        if !Arc::ptr_eq(&upper_inode.fs(), &workdir_inode.fs())
            || Self::layers_overlap_either_direction(&upper_inode, &workdir_inode)?
        {
            return Err(SystemError::EINVAL);
        }
        for (i, (_, lower_inode)) in lower_roots.iter().enumerate() {
            if Self::layer_is_same_or_descendant_of(lower_inode, &upper_inode)?
                || Self::layer_is_same_or_descendant_of(lower_inode, &workdir_inode)?
            {
                return Err(SystemError::ELOOP);
            }

            for (_, other_lower_inode) in lower_roots.iter().skip(i + 1) {
                if Self::layers_overlap_either_direction(lower_inode, other_lower_inode)? {
                    return Err(SystemError::ELOOP);
                }
            }
        }

        if lower_roots.is_empty() {
            return Err(SystemError::EINVAL);
        }

        let mut layers = Vec::new();
        layers.push(upper_layer);
        layers.extend(lower_layers);

        let upper_backing_fs = Self::canonical_backing_fs(&upper_inode);
        let samefs = lower_roots
            .iter()
            .all(|(_, lower)| Arc::ptr_eq(&upper_backing_fs, &Self::canonical_backing_fs(lower)));
        let root_inode = Arc::new(OvlInode::new(
            String::new(),
            upper_file_type,
            Some(upper_inode),
            lower_roots
                .iter()
                .map(|(_, lower_inode)| lower_inode.clone())
                .collect(),
            (!samefs).then(generate_inode_id),
            Arc::try_new(LinkMutationCoordinator::new()).map_err(|_| SystemError::ENOMEM)?,
        ));

        let super_block = SuperBlock::new(vfs::Magic::OVERLAYFS_MAGIC, 4096, 255);
        let backing_cred = ProcessManager::current_pcb().cred();
        let fs = Arc::new_cyclic(|weak_fs| {
            for layer in &layers {
                layer.mnt.set_fs(weak_fs.clone());
            }
            root_inode.set_fs(weak_fs.clone());

            OverlayFS {
                numlayer: layers.len(),
                numfs: 1,
                numdatalayer: lower_roots.len(),
                layers,
                workdir: workdir_inode,
                root_inode,
                super_block: super_block.clone(),
                mutation_lock: Mutex::new(()),
                backing_cred,
                samefs,
                inode_cache: Mutex::new(OvlInodeCache::default()),
                link_state_cache: Mutex::new(OvlLinkStateCache::default()),
                link_seed_connectors_present: AtomicBool::new(false),
                dir_state_cache: Mutex::new(DirStateCache::default()),
                ancestor_publications: Mutex::new(AncestorPublicationCache::default()),
                copy_up_locks: (0..LOCK_STRIPE_COUNT).map(|_| Mutex::new(())).collect(),
                ancestor_copy_up_locks: (0..LOCK_STRIPE_COUNT).map(|_| Mutex::new(())).collect(),
                content_locks: (0..LOCK_STRIPE_COUNT).map(|_| Mutex::new(())).collect(),
            }
        });
        fs.root_inode.load_origin_once()?;
        Ok(fs)
    }

    fn make_mount_data(
        raw_data: Option<&str>,
        _source: &str,
    ) -> Result<Option<Arc<dyn FileSystemMakerData + 'static>>, SystemError> {
        let mount_data = OverlayMountData::from_raw(raw_data).map_err(|e| {
            log::error!("Failed to create overlay mount data: {:?}", e);
            e
        })?;
        Ok(Some(Arc::new(mount_data)))
    }
}

impl OverlayFS {
    fn layer_is_same_or_descendant_of(
        layer: &Arc<dyn IndexNode>,
        ancestor: &Arc<dyn IndexNode>,
    ) -> Result<bool, SystemError> {
        Ok(Self::same_mount_inode(layer, ancestor)? || Self::is_mount_ancestor(ancestor, layer)?)
    }
}
