Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions src/uu/shred/src/shred.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ const PATTERN_BUFFER_SIZE: usize = BLOCK_SIZE + PATTERN_LENGTH - 1;

/// Patterns that appear in order for the passes
///
/// They are all extended to 3 bytes for consistency, even though some could be
/// expressed as single bytes.
/// A single-byte pattern is equivalent to a multi-byte pattern of that byte three times.
const PATTERNS: [Pattern; 22] = [
Pattern::Single(b'\x00'),
Pattern::Single(b'\xFF'),
Expand Down Expand Up @@ -440,9 +439,13 @@ fn wipe_file(
pass_sequence.push(PassType::Random);
}
} else {
// First fill it with Patterns, shuffle it, then evenly distribute Random
let n_full_arrays = n_passes / PATTERNS.len(); // How many times can we go through all the patterns?
let remainder = n_passes % PATTERNS.len(); // How many do we get through on our last time through?
// Add initial random to avoid O(n) operation later
pass_sequence.push(PassType::Random);
let n_random = (n_passes / 10).max(3); // Minimum 3 random passes; ratio of 10 after
let n_fixed = n_passes - n_random;
// Fill it with Patterns and all but the first and last random, then shuffle it
let n_full_arrays = n_fixed / PATTERNS.len(); // How many times can we go through all the patterns?
let remainder = n_fixed % PATTERNS.len(); // How many do we get through on our last time through, excluding randoms?

for _ in 0..n_full_arrays {
for p in PATTERNS {
Expand All @@ -452,14 +455,14 @@ fn wipe_file(
for pattern in PATTERNS.into_iter().take(remainder) {
pass_sequence.push(PassType::Pattern(pattern));
}
let mut rng = rand::rng();
pass_sequence.shuffle(&mut rng); // randomize the order of application

let n_random = 3 + n_passes / 10; // Minimum 3 random passes; ratio of 10 after
// Evenly space random passes; ensures one at the beginning and end
for i in 0..n_random {
pass_sequence[i * (n_passes - 1) / (n_random - 1)] = PassType::Random;
// add random passes except one each at the beginning and end
for _ in 0..n_random - 2 {
pass_sequence.push(PassType::Random);
}

let mut rng = rand::rng();
pass_sequence[1..].shuffle(&mut rng); // randomize the order of application
pass_sequence.push(PassType::Random); // add the last random pass
}

// --zero specifies whether we want one final pass of 0x00 on our file
Expand Down
21 changes: 21 additions & 0 deletions tests/by-util/test_shred.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ use uutests::new_ucmd;
use uutests::util::TestScenario;
use uutests::util_name;

const PATTERNS: [&str; 22] = [
"000000", "ffffff", "555555", "aaaaaa", "249249", "492492", "6db6db", "924924", "b6db6d",
"db6db6", "111111", "222222", "333333", "444444", "666666", "777777", "888888", "999999",
"bbbbbb", "cccccc", "dddddd", "eeeeee",
];

#[test]
fn test_invalid_arg() {
new_ucmd!().arg("--definitely-invalid").fails_with_code(1);
Expand Down Expand Up @@ -241,3 +247,18 @@ fn test_shred_verbose_no_padding_10() {
.succeeds()
.stderr_contains("shred: foo: pass 1/10 (random)...\n");
}

#[test]
fn test_all_patterns_present() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;

let file = "foo.txt";
at.write(file, "bar");

let result = scene.ucmd().arg("-vn25").arg(file).succeeds();

for pat in PATTERNS {
result.stderr_contains(pat);
}
}
Loading