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

shred: fix random passes* (#7830)

* shred: fix random passes, update documentation, add test

* shred: update tests
This commit is contained in:
Jeremy Smart 2025-04-24 03:15:36 -04:00 committed by GitHub
parent 151b196f50
commit b151e039ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 36 additions and 12 deletions

View file

@ -51,8 +51,7 @@ const PATTERN_BUFFER_SIZE: usize = BLOCK_SIZE + PATTERN_LENGTH - 1;
/// Patterns that appear in order for the passes /// Patterns that appear in order for the passes
/// ///
/// They are all extended to 3 bytes for consistency, even though some could be /// A single-byte pattern is equivalent to a multi-byte pattern of that byte three times.
/// expressed as single bytes.
const PATTERNS: [Pattern; 22] = [ const PATTERNS: [Pattern; 22] = [
Pattern::Single(b'\x00'), Pattern::Single(b'\x00'),
Pattern::Single(b'\xFF'), Pattern::Single(b'\xFF'),
@ -440,9 +439,13 @@ fn wipe_file(
pass_sequence.push(PassType::Random); pass_sequence.push(PassType::Random);
} }
} else { } else {
// First fill it with Patterns, shuffle it, then evenly distribute Random // Add initial random to avoid O(n) operation later
let n_full_arrays = n_passes / PATTERNS.len(); // How many times can we go through all the patterns? pass_sequence.push(PassType::Random);
let remainder = n_passes % PATTERNS.len(); // How many do we get through on our last time through? 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 _ in 0..n_full_arrays {
for p in PATTERNS { for p in PATTERNS {
@ -452,14 +455,14 @@ fn wipe_file(
for pattern in PATTERNS.into_iter().take(remainder) { for pattern in PATTERNS.into_iter().take(remainder) {
pass_sequence.push(PassType::Pattern(pattern)); pass_sequence.push(PassType::Pattern(pattern));
} }
let mut rng = rand::rng(); // add random passes except one each at the beginning and end
pass_sequence.shuffle(&mut rng); // randomize the order of application for _ in 0..n_random - 2 {
pass_sequence.push(PassType::Random);
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;
} }
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 // --zero specifies whether we want one final pass of 0x00 on our file

View file

@ -10,6 +10,12 @@ use uutests::new_ucmd;
use uutests::util::TestScenario; use uutests::util::TestScenario;
use uutests::util_name; 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] #[test]
fn test_invalid_arg() { fn test_invalid_arg() {
new_ucmd!().arg("--definitely-invalid").fails_with_code(1); new_ucmd!().arg("--definitely-invalid").fails_with_code(1);
@ -241,3 +247,18 @@ fn test_shred_verbose_no_padding_10() {
.succeeds() .succeeds()
.stderr_contains("shred: foo: pass 1/10 (random)...\n"); .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);
}
}