1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

shred: rename FilenameGenerator to FilenameIter and refactor it a bit

This commit is contained in:
Terts Diepraam 2023-03-10 21:53:59 +01:00
parent cec92046ca
commit 2ac4ee820e

View file

@ -9,15 +9,10 @@
// spell-checker:ignore (words) wipesync // spell-checker:ignore (words) wipesync
use clap::{crate_version, Arg, ArgAction, Command}; use clap::{crate_version, Arg, ArgAction, Command};
use rand::prelude::SliceRandom; use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng};
use rand::{rngs::StdRng, Rng, SeedableRng}; use std::fs::{self, File, OpenOptions};
use std::cell::{Cell, RefCell}; use std::io::{self, Seek, Write};
use std::fs; use std::os::unix::prelude::PermissionsExt;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::prelude::*;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError, UUsageError}; use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
@ -86,61 +81,61 @@ enum PassType {
Random, Random,
} }
// Used to generate all possible filenames of a certain length using NAME_CHARSET as an alphabet /// Iterates over all possible filenames of a certain length using NAME_CHARSET as an alphabet
struct FilenameGenerator { struct FilenameIter {
name_len: usize, // Store the indices of the letters of our filename in NAME_CHARSET
name_charset_indices: RefCell<Vec<usize>>, // Store the indices of the letters of our filename in NAME_CHARSET name_charset_indices: Vec<usize>,
exhausted: Cell<bool>, exhausted: bool,
} }
impl FilenameGenerator { impl FilenameIter {
fn new(name_len: usize) -> Self { fn new(name_len: usize) -> Self {
let indices: Vec<usize> = vec![0; name_len];
Self { Self {
name_len, name_charset_indices: vec![0; name_len],
name_charset_indices: RefCell::new(indices), exhausted: false,
exhausted: Cell::new(false),
} }
} }
} }
impl Iterator for FilenameGenerator { impl Iterator for FilenameIter {
type Item = String; type Item = String;
fn next(&mut self) -> Option<String> { fn next(&mut self) -> Option<String> {
if self.exhausted.get() { if self.exhausted {
return None; return None;
} }
let mut name_charset_indices = self.name_charset_indices.borrow_mut(); // First, make the return value using the current state
let ret: String = self
.name_charset_indices
.iter()
.map(|i| char::from(NAME_CHARSET[*i]))
.collect();
// Make the return value, then increment // Now increment the least significant index and possibly each next
let mut ret = String::new(); // index if necessary.
for i in name_charset_indices.iter() { for index in self.name_charset_indices.iter_mut().rev() {
let c = char::from(NAME_CHARSET[*i]); if *index == NAME_CHARSET.len() - 1 {
ret.push(c); // Carry the 1
} *index = 0;
if name_charset_indices[0] == NAME_CHARSET.len() - 1 {
self.exhausted.set(true);
}
// Now increment the least significant index
for i in (0..self.name_len).rev() {
if name_charset_indices[i] == NAME_CHARSET.len() - 1 {
name_charset_indices[i] = 0; // Carry the 1
continue; continue;
} else { } else {
name_charset_indices[i] += 1; *index += 1;
break; return Some(ret);
} }
} }
// If we get here, we flipped all bits back to 0, so we exhausted all options.
self.exhausted = true;
Some(ret) Some(ret)
} }
} }
// Used to generate blocks of bytes of size <= BLOCK_SIZE based on either a give pattern /// Used to generate blocks of bytes of size <= BLOCK_SIZE based on either a give pattern
// or randomness /// or randomness
// The lint warns about a large difference because StdRng is big, but the buffers are much
// larger anyway, so it's fine.
#[allow(clippy::large_enum_variant)]
enum BytesWriter { enum BytesWriter {
Random { Random {
rng: StdRng, rng: StdRng,
@ -162,7 +157,7 @@ enum BytesWriter {
} }
impl BytesWriter { impl BytesWriter {
fn from_pass_type(pass: PassType) -> Self { fn from_pass_type(pass: &PassType) -> Self {
match pass { match pass {
PassType::Random => Self::Random { PassType::Random => Self::Random {
rng: StdRng::from_entropy(), rng: StdRng::from_entropy(),
@ -173,11 +168,11 @@ impl BytesWriter {
// We prefill the pattern so that the buffer can be reused at each // We prefill the pattern so that the buffer can be reused at each
// iteration as a small optimization. // iteration as a small optimization.
let buffer = match pattern { let buffer = match pattern {
Pattern::Single(byte) => [byte; PATTERN_BUFFER_SIZE], Pattern::Single(byte) => [*byte; PATTERN_BUFFER_SIZE],
Pattern::Multi(bytes) => { Pattern::Multi(bytes) => {
let mut buf = [0; PATTERN_BUFFER_SIZE]; let mut buf = [0; PATTERN_BUFFER_SIZE];
for chunk in buf.chunks_exact_mut(PATTERN_LENGTH) { for chunk in buf.chunks_exact_mut(PATTERN_LENGTH) {
chunk.copy_from_slice(&bytes); chunk.copy_from_slice(bytes);
} }
buf buf
} }
@ -375,7 +370,7 @@ fn wipe_file(
force: bool, force: bool,
) -> UResult<()> { ) -> UResult<()> {
// Get these potential errors out of the way first // Get these potential errors out of the way first
let path: &Path = Path::new(path_str); let path = Path::new(path_str);
if !path.exists() { if !path.exists() {
return Err(USimpleError::new( return Err(USimpleError::new(
1, 1,
@ -445,8 +440,8 @@ fn wipe_file(
pass_sequence.push(PassType::Pattern(PATTERNS[0])); pass_sequence.push(PassType::Pattern(PATTERNS[0]));
} }
let total_passes: usize = pass_sequence.len(); let total_passes = pass_sequence.len();
let mut file: File = OpenOptions::new() let mut file = OpenOptions::new()
.write(true) .write(true)
.truncate(false) .truncate(false)
.open(path) .open(path)
@ -459,8 +454,8 @@ fn wipe_file(
for (i, pass_type) in pass_sequence.into_iter().enumerate() { for (i, pass_type) in pass_sequence.into_iter().enumerate() {
if verbose { if verbose {
let pass_name: String = pass_name(&pass_type); let pass_name = pass_name(&pass_type);
if total_passes.to_string().len() == 1 { if total_passes < 10 {
show_error!( show_error!(
"{}: pass {}/{} ({})... ", "{}: pass {}/{} ({})... ",
path.maybe_quote(), path.maybe_quote(),
@ -479,9 +474,9 @@ fn wipe_file(
} }
} }
// size is an optional argument for exactly how many bytes we want to shred // size is an optional argument for exactly how many bytes we want to shred
show_if_err!(do_pass(&mut file, pass_type, exact, size)
.map_err_context(|| format!("{}: File write pass failed", path.maybe_quote())));
// Ignore failed writes; just keep trying // Ignore failed writes; just keep trying
show_if_err!(do_pass(&mut file, &pass_type, exact, size)
.map_err_context(|| format!("{}: File write pass failed", path.maybe_quote())));
} }
if remove { if remove {
@ -493,7 +488,7 @@ fn wipe_file(
fn do_pass( fn do_pass(
file: &mut File, file: &mut File,
pass_type: PassType, pass_type: &PassType,
exact: bool, exact: bool,
file_size: u64, file_size: u64,
) -> Result<(), io::Error> { ) -> Result<(), io::Error> {
@ -534,7 +529,9 @@ fn wipe_name(orig_path: &Path, verbose: bool) -> Option<PathBuf> {
let mut last_path = PathBuf::from(orig_path); let mut last_path = PathBuf::from(orig_path);
for length in (1..=file_name_len).rev() { for length in (1..=file_name_len).rev() {
for name in FilenameGenerator::new(length) { // Try all filenames of a given length.
// If every possible filename already exists, just reduce the length and try again
for name in FilenameIter::new(length) {
let new_path = orig_path.with_file_name(name); let new_path = orig_path.with_file_name(name);
// We don't want the filename to already exist (don't overwrite) // We don't want the filename to already exist (don't overwrite)
// If it does, find another name that doesn't // If it does, find another name that doesn't
@ -569,7 +566,7 @@ fn wipe_name(orig_path: &Path, verbose: bool) -> Option<PathBuf> {
return None; return None;
} }
} }
} // If every possible filename already exists, just reduce the length and try again }
} }
Some(last_path) Some(last_path)