From 31bfb7bf4c811ed7ec1564ede11cc69147342efe Mon Sep 17 00:00:00 2001 From: Alexey Zinchenko Date: Sun, 31 Jan 2021 00:34:06 +0300 Subject: [PATCH] Added some flags usage & refactoring. --- src/main.rs | 49 +++++++++++++--- src/shred/mod.rs | 141 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 140 insertions(+), 50 deletions(-) diff --git a/src/main.rs b/src/main.rs index 769863c..0736097 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,9 @@ extern crate clap; use clap::{App, Arg, crate_authors, crate_name, crate_version}; -use crate::shred::{Shredder, Verbosity}; +use crate::shred::{Shredder, Verbosity, ShredOptions}; +use std::process::exit; +use std::str::FromStr; mod shred; @@ -25,7 +27,14 @@ fn main() { .arg(Arg::with_name("i") .short("i") .long("interactive") - .help("Enables interactive mode") + .help("Enables interactive mode")) + .arg(Arg::with_name("k") + .short("k") + .long("keep") + .help("Don't delete files after shredding")) + .arg(Arg::with_name("n") + .short("n") + .help("How many times the file must be overridden") ) .get_matches(); @@ -38,15 +47,41 @@ fn main() { let is_recursively = params.is_present("r"); let is_interactive = params.is_present("i"); + let keep_files = params.is_present("k"); + let iterations_count = if params.is_present("n") { + let value_option = params.value_of("n"); + match value_option { + None => { + println!("No argument passed to the 'n' option!"); + exit(1); + } + Some(value) => { + match u8::from_str(value) { + Ok(number) => { + number + } + Err(error) => { + println!("{}", error.to_string()); + exit(1); + } + } + } + } + } else { + 3 + }; // Calling .unwrap() is safe here because "INPUT" is required (if "INPUT" wasn't // required we could have used an 'if let' to conditionally get the value) let path = params.value_of("PATH").unwrap(); - Shredder::new( - path.to_string(), - is_recursively, - is_interactive, - verbosity + Shredder::with_options( + ShredOptions::new(path.to_string()) + .set_is_interactive(is_interactive) + .set_is_recursive(is_recursively) + .set_keep_files(keep_files) + .set_verbosity(verbosity) + .set_rewrite_iterations(iterations_count) + .build() ).run(); } \ No newline at end of file diff --git a/src/shred/mod.rs b/src/shred/mod.rs index e78f060..d09f8a5 100644 --- a/src/shred/mod.rs +++ b/src/shred/mod.rs @@ -1,52 +1,48 @@ use std::cmp::Ordering; use std::fs; use std::fs::File; -use std::io::{BufWriter, Write}; +use std::io::{BufWriter, Write, Seek, SeekFrom}; use std::io; use std::process::exit; pub struct Shredder { - path: String, - is_recursively: bool, - is_interactive: bool, - verbosity: Verbosity, + options: ShredOptions, } impl Shredder { - pub fn new(path: String, is_recursively: bool, is_interactive: bool, verbosity: Verbosity) -> Shredder { + pub fn with_options(options: ShredOptions) -> Shredder { Shredder { - path, - is_recursively, - is_interactive, - verbosity + options } } pub fn run(&self) { - match fs::metadata(&self.path) { + let verbosity = &self.options.verbosity; + let metadata_result = fs::metadata(&self.options.path); + match &metadata_result { Ok(metadata) => { - if self.verbosity >= Verbosity::Average { + if *verbosity >= Verbosity::Average { println!("Is directory: {}", metadata.is_dir()); println!("Is file: {}", metadata.is_file()); - if self.verbosity == Verbosity::High { - println!("Is recursively: {}", self.is_recursively); - println!("Is interactive: {}", self.is_interactive); - println!("Verbosity: {:?}", &self.verbosity); + if *verbosity == Verbosity::High { + println!("Is recursively: {}", self.options.is_recursive); + println!("Is interactive: {}", self.options.is_interactive); + println!("Verbosity: {:?}", &self.options.verbosity); } } } Err(_) => { - println!("Provided path is invalid!"); + println!("No such file or directory."); exit(1); } } - if self.verbosity > Verbosity::None { - println!("Using input file: {}", &self.path); + if *verbosity > Verbosity::None { + println!("Using input file: {}", &self.options.path); } - if fs::metadata(&self.path).unwrap().is_file() { - Shredder::shred_file(&self.path, self.is_interactive); - } else if self.is_recursively { + if metadata_result.unwrap().is_file() { + Shredder::shred_file(&self.options); + } else if self.options.is_recursive { } else { println!("Target is a directory!"); @@ -54,37 +50,46 @@ impl Shredder { } } - fn shred_file(path: &String, is_interactive: bool) { - match std::fs::canonicalize(path) { + fn shred_file(options: &ShredOptions) { + match std::fs::canonicalize(&options.path) { Ok(path) => { - if is_interactive { - let file_length = path.metadata().unwrap().len(); - let absolute_path = path.to_str().unwrap(); - println!("Do you really want to shred '{}'? [Y/n]", absolute_path); + let file_length = path.metadata().unwrap().len(); + let absolute_path = path.to_str().unwrap(); + if options.is_interactive { + print!("Do you really want to shred '{}'? [Y/n] ", absolute_path); + io::stdout().flush().unwrap(); let mut input = String::new(); io::stdin().read_line(&mut input).expect("Failed to read input."); let input = input.trim(); - if input.len() == 1 && input.to_lowercase().eq("y") { - match File::create(absolute_path) { - Ok(file) => { - println!("File's size: {}", file_length); - let mut buffer = BufWriter::new(&file); + if input.len() != 1 || !input.to_lowercase().eq("y") { + return; + } + } - let random_bytes: Vec = (0..file_length).map(|_| { - rand::random::() - }).collect(); - buffer.write(&random_bytes).unwrap(); + match File::create(absolute_path) { + Ok(file) => { + println!("File's size: {}", file_length); + let mut buffer = BufWriter::new(&file); - buffer.flush().unwrap(); - file.sync_all().unwrap(); - } - Err(error) => { - println!("{}", error); - } + for _ in 0..options.rewrite_iterations { + let random_bytes: Vec = (0..file_length).map(|_| { + rand::random::() + }).collect(); + buffer.write(&random_bytes).unwrap(); + + buffer.flush().unwrap(); + file.sync_all().unwrap(); + buffer.seek(SeekFrom::Start(0)).unwrap(); } + if !options.keep_files { + fs::remove_file(absolute_path).unwrap(); + } + } + Err(error) => { + println!("{}", error); } } } @@ -95,6 +100,56 @@ impl Shredder { } } +pub struct ShredOptions { + verbosity: Verbosity, + is_recursive: bool, + is_interactive: bool, + rewrite_iterations: u8, + keep_files: bool, + path: String, +} + +impl ShredOptions { + pub fn new(path: String) -> ShredOptions { + ShredOptions { + path, + is_interactive: true, + is_recursive: false, + rewrite_iterations: 3, + keep_files: false, + verbosity: Verbosity::None, + } + } + + pub fn set_verbosity(mut self, verbosity: Verbosity) -> ShredOptions { + self.verbosity = verbosity; + self + } + + pub fn set_is_recursive(mut self, is_recursive: bool) -> ShredOptions { + self.is_recursive = is_recursive; + self + } + + pub fn set_is_interactive(mut self, is_interactive: bool) -> ShredOptions { + self.is_interactive = is_interactive; + self + } + + pub fn set_keep_files(mut self, is_keep_files: bool) -> ShredOptions { + self.keep_files = is_keep_files; + self + } + + pub fn set_rewrite_iterations(mut self, count: u8) -> ShredOptions { + self.rewrite_iterations = count; + self + } + + pub fn build(self) -> ShredOptions { + self + } +} #[derive(Debug, Eq)] pub enum Verbosity {