You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
119 lines
2.5 KiB
119 lines
2.5 KiB
#![allow(dead_code)]
|
|
#![allow(unused_imports)]
|
|
|
|
extern crate sha2;
|
|
|
|
use std::fs::{File};
|
|
use std::fs;
|
|
use std::path::{Path, PathBuf};
|
|
use std::io::prelude::*;
|
|
use sha2::{Sha256};
|
|
|
|
mod hash;
|
|
mod traverse;
|
|
mod pool;
|
|
mod rotating_list;
|
|
|
|
fn funcall<T: FnOnce()>(w: T)
|
|
{
|
|
w();
|
|
}
|
|
|
|
const THREADS: usize = 3;
|
|
//const OUTPUT_DIR: &'static str = "./output";
|
|
//const INPUT_DIR: &'static str = "./target";
|
|
|
|
macro_rules! join_path {
|
|
($($path:expr),*) => {
|
|
{
|
|
let mut buf = std::path::PathBuf::new();
|
|
$(
|
|
buf.push($path);
|
|
)+
|
|
buf
|
|
}
|
|
}
|
|
}
|
|
|
|
fn output_path(input_dir: &str, output_dir: &str, path: &Path) -> Result<PathBuf, &'static str> {
|
|
let mut buf = PathBuf::new();
|
|
buf.push(output_dir);
|
|
if let Some(path) = path.to_str() {
|
|
buf.push(&path[input_dir.len()+1..]);
|
|
Ok(buf)
|
|
} else {
|
|
Err("Bad pathspec")
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
|
|
let args: Vec<String> = std::env::args().collect();
|
|
|
|
if args.len() < 3 {
|
|
println!("hashlink - generate recursive file hashtable to coerce duplicate files to hardlinks.\n");
|
|
println!("Usage: {} <input> <output>", args[0]);
|
|
return;
|
|
}
|
|
|
|
let input_dir = &args[1];
|
|
let output_dir = &args[2];
|
|
|
|
let root = traverse::DirectoryIterator::begin(input_dir).unwrap();
|
|
|
|
let mut pools = create_pools!(THREADS);
|
|
|
|
if !Path::new(output_dir).exists() {
|
|
if let Err(err) = fs::create_dir(output_dir) {
|
|
println!("Could not create output directory: {}", err);
|
|
return;
|
|
}
|
|
}
|
|
|
|
let hardlink_dir = join_path!(output_dir, ".hashlink");
|
|
|
|
if !hardlink_dir.exists() {
|
|
if let Err(err) = fs::create_dir(hardlink_dir.as_path()) {
|
|
println!("Could not create .hashlink directory: {}", err);
|
|
return;
|
|
}
|
|
}
|
|
|
|
for dir in root {
|
|
let hardlink_dir = hardlink_dir.to_path_buf();
|
|
let input_dir = input_dir.to_string();
|
|
let output_dir = output_dir.to_string();
|
|
send!(pools.get(), move || {
|
|
if let Ok(mut file) = File::open(&dir) {
|
|
if let Ok(hash) = hash::FileHash::create(&mut file) {
|
|
|
|
let hlpath = join_path!(hardlink_dir, hash.to_string());
|
|
|
|
if let Ok(dst) = output_path(&input_dir, &output_dir, Path::new(&dir)) {
|
|
|
|
|
|
if let Some(parent) = dst.parent() {
|
|
if !parent.exists() {
|
|
if let Err(_) = fs::create_dir(parent) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if !hlpath.exists() {
|
|
if let Err(_) = std::fs::copy(&dir, &hlpath) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
match fs::hard_link(hlpath, dst) {
|
|
Ok(_) => println!("{} -> {}", dir, hash),
|
|
Err(err) => println!("E: {} failed: {}", dir, err),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|