use cap_fs_ext::DirExt; use cap_std::fs::Dir; use std::ffi::OsString; use std::io; use std::path::{Component, Path, PathBuf}; pub fn parent(path: &Path) -> io::Result<(Dir, OsString)> { let mut root = PathBuf::new(); let mut names = Vec::new(); for component in path.components() { match component { Component::Prefix(_) | Component::RootDir => root.push(component.as_os_str()), Component::Normal(name) => names.push(name), _ => { return Err(io::Error::new( io::ErrorKind::InvalidInput, "Expected a resolved filesystem path", )); } } } let name = names.pop().unwrap_or_else(|| std::ffi::OsStr::new(".")); let mut directory = Dir::open_ambient_dir(root, cap_std::ambient_authority())?; for name in names { directory = match directory.open_dir_nofollow(name) { Ok(directory) => directory, Err(_) if directory .symlink_metadata(name) .is_ok_and(|metadata| metadata.is_symlink()) => { return Err(io::Error::new( io::ErrorKind::NotFound, "Symbolic link changed or has a missing target", )); } Err(error) => return Err(error), }; } Ok((directory, name.to_owned())) }