Skip to content

Commit b151e03

Browse files
authored
shred: fix random passes* (#7830)
* shred: fix random passes, update documentation, add test * shred: update tests
1 parent 151b196 commit b151e03

File tree

2 files changed

+36
-12
lines changed

2 files changed

+36
-12
lines changed

src/uu/shred/src/shred.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ const PATTERN_BUFFER_SIZE: usize = BLOCK_SIZE + PATTERN_LENGTH - 1;
5151

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

447450
for _ in 0..n_full_arrays {
448451
for p in PATTERNS {
@@ -452,14 +455,14 @@ fn wipe_file(
452455
for pattern in PATTERNS.into_iter().take(remainder) {
453456
pass_sequence.push(PassType::Pattern(pattern));
454457
}
455-
let mut rng = rand::rng();
456-
pass_sequence.shuffle(&mut rng); // randomize the order of application
457-
458-
let n_random = 3 + n_passes / 10; // Minimum 3 random passes; ratio of 10 after
459-
// Evenly space random passes; ensures one at the beginning and end
460-
for i in 0..n_random {
461-
pass_sequence[i * (n_passes - 1) / (n_random - 1)] = PassType::Random;
458+
// add random passes except one each at the beginning and end
459+
for _ in 0..n_random - 2 {
460+
pass_sequence.push(PassType::Random);
462461
}
462+
463+
let mut rng = rand::rng();
464+
pass_sequence[1..].shuffle(&mut rng); // randomize the order of application
465+
pass_sequence.push(PassType::Random); // add the last random pass
463466
}
464467

465468
// --zero specifies whether we want one final pass of 0x00 on our file

tests/by-util/test_shred.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ use uutests::new_ucmd;
1010
use uutests::util::TestScenario;
1111
use uutests::util_name;
1212

13+
const PATTERNS: [&str; 22] = [
14+
"000000", "ffffff", "555555", "aaaaaa", "249249", "492492", "6db6db", "924924", "b6db6d",
15+
"db6db6", "111111", "222222", "333333", "444444", "666666", "777777", "888888", "999999",
16+
"bbbbbb", "cccccc", "dddddd", "eeeeee",
17+
];
18+
1319
#[test]
1420
fn test_invalid_arg() {
1521
new_ucmd!().arg("--definitely-invalid").fails_with_code(1);
@@ -241,3 +247,18 @@ fn test_shred_verbose_no_padding_10() {
241247
.succeeds()
242248
.stderr_contains("shred: foo: pass 1/10 (random)...\n");
243249
}
250+
251+
#[test]
252+
fn test_all_patterns_present() {
253+
let scene = TestScenario::new(util_name!());
254+
let at = &scene.fixtures;
255+
256+
let file = "foo.txt";
257+
at.write(file, "bar");
258+
259+
let result = scene.ucmd().arg("-vn25").arg(file).succeeds();
260+
261+
for pat in PATTERNS {
262+
result.stderr_contains(pat);
263+
}
264+
}

0 commit comments

Comments
 (0)