use crate::error::Error;
use crate::error::Result;
use crate::unmount;
use std::fs;
use std::io;
use std::io::ErrorKind;
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
pub fn remove<P: AsRef<Path>>(path: P) -> Result<()> {
let path = path.as_ref();
let normalized: PathBuf = path
.components()
.filter(|c| !matches!(c, std::path::Component::CurDir))
.collect();
let last_segment = normalized.components().last().ok_or_else(|| {
Error::InvalidTarget("Invalid path, cannot get last file path component".to_string())
})?;
if matches!(last_segment, std::path::Component::ParentDir) {
return Err(Error::InvalidTarget(
"Invalid path, last path segment cannot be \"..\"".to_string(),
));
}
match normalized.symlink_metadata() {
Ok(_) => {
unmount_nested(&normalized)?;
recursive_remove(&normalized).map_err(Error::IoError)
}
Err(err) => match err.kind() {
ErrorKind::NotFound => Err(Error::NotFound),
_ => Err(Error::IoError(err)),
},
}
}
pub fn ensure_removed<P: AsRef<Path>>(path: P) -> Result<()> {
if let Err(err) = path.as_ref().symlink_metadata() {
if err.kind() == ErrorKind::NotFound {
return Ok(());
}
};
remove(path)
}
fn unmount_nested(path: &Path) -> Result<()> {
for mp in unmount::mounts_under(path)? {
unmount::unmount_lazy(&mp)?;
}
Ok(())
}
fn recursive_remove(path: &Path) -> io::Result<()> {
fix_permissions(path)?;
let metadata = path.symlink_metadata()?;
if !metadata.is_dir() {
fs::remove_file(path)
} else if fs::remove_dir(path).is_ok() {
Ok(())
} else {
for child in fs::read_dir(&path)? {
let child = child?;
let path = child.path();
stacker::maybe_grow(4 * 1024, 16 * 1024, || recursive_remove(&path))?;
}
fs::remove_dir(path)
}
}
fn fix_permissions(path: &Path) -> io::Result<()> {
let metadata = fs::symlink_metadata(path)?;
let mut perms = metadata.permissions();
let mode = perms.mode();
if mode & 0o700 != 0o700 {
perms.set_mode(mode | 0o700);
fs::set_permissions(path, perms)?;
}
Ok(())
}