#![warn(clippy::all, clippy::pedantic)]
mod args;
mod calibrate;
mod interrupt;
mod progress;
mod walk;
use anyhow::{Context, Error, Result};
use cfg_if::cfg_if;
use clap::Parser;
use fs_err as fs;
use humantime::Duration as HumanDuration;
use std::collections::HashSet;
use std::os::unix::fs::MetadataExt;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use std::time::Instant;
use tempfile::TempDir;
cfg_if! {
if #[cfg(all(target_os = "linux", target_arch = "x86_64"))] {
use_mimalloc!();
} else if #[cfg(all(target_os = "linux", target_arch = "aarch64"))] {
use_mimalloc!();
} else if #[cfg(target_os = "macos")] {
use_mimalloc!();
}
}
fn main() -> Result<(), Error> {
let args = Arc::new(args::Args::parse());
let shutdown = Arc::new(AtomicBool::new(false));
let shutdown_scan = shutdown.clone();
interrupt::setup_interrupt_handler(shutdown)?;
let mut visited_paths = HashSet::with_capacity(args.path.len());
for path in args.path.clone() {
match visited_paths.get(&path) {
None => visited_paths.insert(path.clone()),
_ => continue,
};
println!("Started analysis for path {}", path.display());
let path_metadata = fs::metadata(&path)?;
let size_inode_ratio = if args.size_inode_ratio > 0 {
args.size_inode_ratio
} else if let Some(ref user_path) = args.calibration_path {
if fs::metadata(user_path.as_path())?.dev() != path_metadata.dev() {
println!(
"Oops, test directory resides on a different device than path {}, results are possibly unreliable!",
path.display()
);
}
let tmp_dir = Arc::new(
TempDir::new_in(user_path.as_path())
.context("Unable to setup/create calibration test directory")?,
);
calibrate::get_inode_ratio(tmp_dir.path(), &shutdown_scan, &args)
.context("Unable to calibrate inode to size ratio")?
} else {
let tmp_dir = Arc::new(
TempDir::new_in(path.as_path())
.context("Unable to setup/create calibration test directory")?,
);
calibrate::get_inode_ratio(tmp_dir.path(), &shutdown_scan, &args)
.context("Unable to calibrate inode to size ratio")?
};
let start = Instant::now();
let pb = progress::new_spinner(format!("Scanning path {} in progress...", path.display()));
walk::parallel_search(
&path,
path_metadata,
size_inode_ratio,
shutdown_scan.clone(),
args.clone(),
)?;
pb.finish_with_message("Done.");
println!(
"Scanning path {} completed. Time elapsed: {}",
path.display(),
HumanDuration::from(start.elapsed())
);
}
Ok(())
}
#[macro_export]
macro_rules! use_mimalloc {
() => {
use mimalloc::MiMalloc;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
};
}