use std::path::{Component, Path, PathBuf}; use anyhow::{Result, bail}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct RootfsPath(PathBuf); impl RootfsPath { pub fn new(path: impl AsRef) -> Result { let path = path.as_ref(); if path.as_os_str().is_empty() || path.is_absolute() { bail!( "rootfs path must be a non-empty relative path: {}", path.display() ); } let mut normalized = PathBuf::new(); for component in path.components() { match component { Component::Normal(component) => normalized.push(component), Component::CurDir => {} Component::ParentDir | Component::RootDir | Component::Prefix(_) => { bail!("rootfs path cannot escape the rootfs: {}", path.display()); } } } Ok(Self(normalized)) } pub fn as_path(&self) -> &Path { &self.0 } } pub fn relative_symlink_is_safe(link_parent: &Path, target: &Path) -> bool { if target.is_absolute() { return false; } let mut depth = link_parent.components().count(); for component in target.components() { match component { Component::Normal(_) | Component::CurDir => {} Component::ParentDir => { if depth == 0 { return false; } depth -= 1; } Component::RootDir | Component::Prefix(_) => return false, } } true }