use std::{
fs::{self, OpenOptions},
io,
mem::size_of,
os::windows::prelude::*,
path::{Path, PathBuf},
};
use rayon::prelude::*;
use winapi::shared::minwindef::*;
use winapi::um::fileapi::*;
use winapi::um::minwinbase::*;
use winapi::um::winbase::*;
use winapi::um::winnt::*;
pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
let path = _remove_dir_contents(path)?;
let metadata = path.metadata()?;
if metadata.permissions().readonly() {
delete_readonly(metadata, &path)?;
} else {
log::trace!("removing {}", &path.display());
fs::remove_dir(&path).map_err(|e| {
log::debug!("error removing {}", &path.display());
e
})?;
log::trace!("removed {}", &path.display());
}
Ok(())
}
pub fn _remove_dir_contents<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
let path = path.as_ref().canonicalize()?;
_delete_dir_contents(&path)?;
Ok(path)
}
fn _delete_dir_contents(path: &PathBuf) -> io::Result<()> {
log::trace!("scanning {}", &path.display());
let iter = path.read_dir()?.par_bridge();
iter.try_for_each(|dir_entry| -> io::Result<()> {
let dir_entry = dir_entry?;
let metadata = dir_entry.metadata()?;
let is_dir = dir_entry.file_type()?.is_dir();
let dir_path = dir_entry.path();
if is_dir {
_delete_dir_contents(&dir_path)?;
}
log::trace!("removing {}", &dir_path.display());
if metadata.permissions().readonly() {
delete_readonly(metadata, &dir_path).map_err(|e| {
log::debug!("error removing {}", &dir_path.display());
e
})?;
} else if is_dir {
fs::remove_dir(&dir_path).map_err(|e| {
log::debug!("error removing {}", &dir_path.display());
e
})?;
} else {
fs::remove_file(&dir_path).map_err(|e| {
log::debug!("error removing {}", &dir_path.display());
e
})?;
}
log::trace!("removed {}", &dir_path.display());
Ok(())
})?;
log::trace!("scanned {}", &path.display());
Ok(())
}
fn delete_readonly(metadata: fs::Metadata, path: &Path) -> io::Result<()> {
let mut opts = OpenOptions::new();
opts.access_mode(DELETE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES);
opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT);
let file = opts.open(path)?;
let mut perms = metadata.permissions();
perms.set_readonly(false);
file.set_permissions(perms)?;
let mut info = FILE_DISPOSITION_INFO {
DeleteFile: TRUE as u8,
};
let result = unsafe {
SetFileInformationByHandle(
file.as_raw_handle(),
FileDispositionInfo,
&mut info as *mut FILE_DISPOSITION_INFO as LPVOID,
size_of::<FILE_DISPOSITION_INFO>() as u32,
)
};
if result == 0 {
return Err(io::Error::last_os_error());
}
file.set_permissions(metadata.permissions())?;
drop(file);
Ok(())
}