Skip to content

Commit a74b7ea

Browse files
committed
feat: implement --random-source for shred
1 parent e061cb7 commit a74b7ea

File tree

3 files changed

+51
-2
lines changed

3 files changed

+51
-2
lines changed

src/uu/shred/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ path = "src/shred.rs"
1919
[dependencies]
2020
clap = { workspace = true }
2121
rand = { workspace = true }
22-
uucore = { workspace = true }
22+
uucore = { workspace = true, features = ["rand-read"] }
2323
libc = { workspace = true }
2424

2525
[[bin]]

src/uu/shred/src/shred.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use std::path::{Path, PathBuf};
1717
use uucore::display::Quotable;
1818
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
1919
use uucore::parse_size::parse_size_u64;
20+
use uucore::rand_read::{ReadRng, WrappedRng};
2021
use uucore::shortcut_value_parser::ShortcutValueParser;
2122
use uucore::{format_usage, help_about, help_section, help_usage, show_error, show_if_err};
2223

@@ -34,6 +35,7 @@ pub mod options {
3435
pub const VERBOSE: &str = "verbose";
3536
pub const EXACT: &str = "exact";
3637
pub const ZERO: &str = "zero";
38+
pub const RANDOM_SOURCE: &str = "random-source";
3739

3840
pub mod remove {
3941
pub const UNLINK: &str = "unlink";
@@ -261,6 +263,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
261263
let exact = matches.get_flag(options::EXACT) || size.is_some();
262264
let zero = matches.get_flag(options::ZERO);
263265
let verbose = matches.get_flag(options::VERBOSE);
266+
let random_source = matches
267+
.get_one::<String>(options::RANDOM_SOURCE)
268+
.map(String::from);
264269

265270
for path_str in matches.get_many::<String>(options::FILE).unwrap() {
266271
show_if_err!(wipe_file(
@@ -272,6 +277,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
272277
zero,
273278
verbose,
274279
force,
280+
&random_source
275281
));
276282
}
277283
Ok(())
@@ -357,6 +363,13 @@ pub fn uu_app() -> Command {
357363
.action(ArgAction::Append)
358364
.value_hint(clap::ValueHint::FilePath),
359365
)
366+
.arg(
367+
Arg::new(options::RANDOM_SOURCE)
368+
.long(options::RANDOM_SOURCE)
369+
.value_name("FILE")
370+
.help("get random bytes from FILE")
371+
.value_hint(clap::ValueHint::FilePath),
372+
)
360373
}
361374

362375
fn get_size(size_str_opt: Option<String>) -> Option<u64> {
@@ -392,6 +405,7 @@ fn wipe_file(
392405
zero: bool,
393406
verbose: bool,
394407
force: bool,
408+
random_source: &Option<String>,
395409
) -> UResult<()> {
396410
// Get these potential errors out of the way first
397411
let path = Path::new(path_str);
@@ -452,7 +466,17 @@ fn wipe_file(
452466
for pattern in PATTERNS.into_iter().take(remainder) {
453467
pass_sequence.push(PassType::Pattern(pattern));
454468
}
455-
let mut rng = rand::thread_rng();
469+
470+
let mut rng = match random_source {
471+
Some(r) => {
472+
let file = File::open(&r[..]).map_err_context(|| {
473+
format!("failed to open random source {}", r.quote())
474+
})?;
475+
WrappedRng::RngFile(ReadRng::new(file))
476+
}
477+
None => WrappedRng::RngDefault(rand::thread_rng()),
478+
};
479+
456480
pass_sequence.shuffle(&mut rng); // randomize the order of application
457481

458482
let n_random = 3 + n_passes / 10; // Minimum 3 random passes; ratio of 10 after
@@ -501,6 +525,7 @@ fn wipe_file(
501525
do_remove(path, path_str, verbose, remove_method)
502526
.map_err_context(|| format!("{}: failed to remove file", path.maybe_quote()))?;
503527
}
528+
504529
Ok(())
505530
}
506531

tests/by-util/test_shred.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,30 @@ fn test_shred_empty() {
181181
assert!(!at.file_exists(file_a));
182182
}
183183

184+
#[test]
185+
fn test_random_source() {
186+
let (at, mut ucmd) = at_and_ucmd!();
187+
188+
let file = "test_random_source";
189+
let file_original_content = "test_shred file content";
190+
191+
at.write(file, file_original_content);
192+
193+
// Write to random file
194+
let random_file = "test_random_file";
195+
at.touch(random_file);
196+
at.write(random_file, "random contents");
197+
198+
ucmd.arg("--random-source=test_random_file")
199+
.arg(file)
200+
.succeeds();
201+
202+
// File exists
203+
assert!(at.file_exists(file));
204+
// File is obfuscated
205+
assert!(at.read_bytes(file) != file_original_content.as_bytes());
206+
}
207+
184208
#[test]
185209
#[cfg(all(unix, feature = "chmod"))]
186210
fn test_shred_fail_no_perm() {

0 commit comments

Comments
 (0)