From 794546fe3ae1e14d235bceebb87b823d20dd87c2 Mon Sep 17 00:00:00 2001 From: Alexey Zinchenko Date: Tue, 26 Jan 2021 13:02:48 +0300 Subject: [PATCH] Implemented basic shreding functionality. --- Cargo.lock | 70 +++++++++++++++++++++++++ Cargo.toml | 3 +- src/main.rs | 43 ++++++--------- src/shred/mod.rs | 134 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 221 insertions(+), 29 deletions(-) create mode 100644 src/shred/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 12a69ea..64d8dd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "clap" version = "2.33.3" @@ -41,6 +47,17 @@ dependencies = [ "vec_map", ] +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "hermit-abi" version = "0.1.18" @@ -56,11 +73,58 @@ version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core", +] + [[package]] name = "rshred" version = "0.1.0" dependencies = [ "clap", + "rand", ] [[package]] @@ -90,6 +154,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "wasi" +version = "0.10.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c6c3420963c5c64bca373b25e77acb562081b9bb4dd5bb864187742186cea9" + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 26666a4..5648fec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,5 @@ license-file = "LICENSE" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = "2.33.3" \ No newline at end of file +clap = "2.33.3" +rand = "0.8.3" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index bf16a00..769863c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,15 @@ extern crate clap; -use clap::{App, Arg}; -use std::fs; -use std::process::exit; + +use clap::{App, Arg, crate_authors, crate_name, crate_version}; + +use crate::shred::{Shredder, Verbosity}; + +mod shred; fn main() { - let params = App::new("shred") - .version("0.1.0") - .author("Alexey Zinchenko ") + let params = App::new(crate_name!()) + .version(crate_version!()) + .author(crate_authors!()) .about("TODO") .arg(Arg::with_name("PATH") .help("Sets the path of file or directory to use") @@ -39,27 +42,11 @@ fn main() { // 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(); - match fs::metadata(path) { - Ok(metadata) => { - println!("Exists: OK"); - println!("Is directory: {}", metadata.is_dir()); - println!("Is file: {}", metadata.is_file()); - println!("Is recursively: {}", is_recursively); - println!("Is interactive: {}", is_interactive); - println!("Verbosity: {:?}", verbosity); - } - Err(_) => { - println!("Provided path is invalid!"); - exit(1); - } - } - println!("Using input file: {}", path); -} -#[derive(Debug)] -enum Verbosity { - None, - Low, - Average, - High + Shredder::new( + path.to_string(), + is_recursively, + is_interactive, + verbosity + ).run(); } \ No newline at end of file diff --git a/src/shred/mod.rs b/src/shred/mod.rs new file mode 100644 index 0000000..e78f060 --- /dev/null +++ b/src/shred/mod.rs @@ -0,0 +1,134 @@ +use std::cmp::Ordering; +use std::fs; +use std::fs::File; +use std::io::{BufWriter, Write}; +use std::io; +use std::process::exit; + +pub struct Shredder { + path: String, + is_recursively: bool, + is_interactive: bool, + verbosity: Verbosity, +} + +impl Shredder { + pub fn new(path: String, is_recursively: bool, is_interactive: bool, verbosity: Verbosity) -> Shredder { + Shredder { + path, + is_recursively, + is_interactive, + verbosity + } + } + + pub fn run(&self) { + match fs::metadata(&self.path) { + Ok(metadata) => { + if self.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); + } + } + } + Err(_) => { + println!("Provided path is invalid!"); + exit(1); + } + } + if self.verbosity > Verbosity::None { + println!("Using input file: {}", &self.path); + } + + if fs::metadata(&self.path).unwrap().is_file() { + Shredder::shred_file(&self.path, self.is_interactive); + } else if self.is_recursively { + + } else { + println!("Target is a directory!"); + exit(1); + } + } + + fn shred_file(path: &String, is_interactive: bool) { + match std::fs::canonicalize(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 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); + + let random_bytes: Vec = (0..file_length).map(|_| { + rand::random::() + }).collect(); + buffer.write(&random_bytes).unwrap(); + + buffer.flush().unwrap(); + file.sync_all().unwrap(); + } + Err(error) => { + println!("{}", error); + } + } + + } + } + } + Err(error) => { + println!("{}", error); + } + } + } +} + + +#[derive(Debug, Eq)] +pub enum Verbosity { + None, + Low, + Average, + High +} + +impl Verbosity { + pub fn discriminant(&self) -> i8 { + match self { + Verbosity::None => 0, + Verbosity::Low => 1, + Verbosity::Average => 2, + Verbosity::High => 3, + } + } +} + +impl Ord for Verbosity { + fn cmp(&self, other: &Self) -> Ordering { + self.discriminant().cmp(&other.discriminant()) + } +} + +impl PartialOrd for Verbosity { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.discriminant().cmp(&other.discriminant())) + } +} + +impl PartialEq for Verbosity { + fn eq(&self, other: &Self) -> bool { + self.discriminant() == other.discriminant() + } +}