diff --git a/Cargo.lock b/Cargo.lock index 435d1a39d73..11055af6ca3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3509,6 +3509,8 @@ dependencies = [ "number_prefix", "once_cell", "os_display", + "rand", + "rand_core", "regex", "sha1", "sha2", diff --git a/src/uu/shred/Cargo.toml b/src/uu/shred/Cargo.toml index 0f5f1d6aab8..c6557dc376d 100644 --- a/src/uu/shred/Cargo.toml +++ b/src/uu/shred/Cargo.toml @@ -19,7 +19,7 @@ path = "src/shred.rs" [dependencies] clap = { workspace = true } rand = { workspace = true } -uucore = { workspace = true } +uucore = { workspace = true, features = ["rand-read"] } libc = { workspace = true } [[bin]] diff --git a/src/uu/shred/src/shred.rs b/src/uu/shred/src/shred.rs index 763d6cfd4bf..f66a9dbc7fd 100644 --- a/src/uu/shred/src/shred.rs +++ b/src/uu/shred/src/shred.rs @@ -17,6 +17,7 @@ use std::path::{Path, PathBuf}; use uucore::display::Quotable; use uucore::error::{FromIo, UResult, USimpleError, UUsageError}; use uucore::parse_size::parse_size_u64; +use uucore::rand_read::{ReadRng, WrappedRng}; use uucore::shortcut_value_parser::ShortcutValueParser; use uucore::{format_usage, help_about, help_section, help_usage, show_error, show_if_err}; @@ -34,6 +35,7 @@ pub mod options { pub const VERBOSE: &str = "verbose"; pub const EXACT: &str = "exact"; pub const ZERO: &str = "zero"; + pub const RANDOM_SOURCE: &str = "random-source"; pub mod remove { pub const UNLINK: &str = "unlink"; @@ -261,6 +263,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let exact = matches.get_flag(options::EXACT) || size.is_some(); let zero = matches.get_flag(options::ZERO); let verbose = matches.get_flag(options::VERBOSE); + let random_source = matches + .get_one::(options::RANDOM_SOURCE) + .map(String::from); for path_str in matches.get_many::(options::FILE).unwrap() { show_if_err!(wipe_file( @@ -272,6 +277,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { zero, verbose, force, + &random_source )); } Ok(()) @@ -357,6 +363,13 @@ pub fn uu_app() -> Command { .action(ArgAction::Append) .value_hint(clap::ValueHint::FilePath), ) + .arg( + Arg::new(options::RANDOM_SOURCE) + .long(options::RANDOM_SOURCE) + .value_name("FILE") + .help("get random bytes from FILE") + .value_hint(clap::ValueHint::FilePath), + ) } fn get_size(size_str_opt: Option) -> Option { @@ -392,6 +405,7 @@ fn wipe_file( zero: bool, verbose: bool, force: bool, + random_source: &Option, ) -> UResult<()> { // Get these potential errors out of the way first let path = Path::new(path_str); @@ -452,7 +466,17 @@ fn wipe_file( for pattern in PATTERNS.into_iter().take(remainder) { pass_sequence.push(PassType::Pattern(pattern)); } - let mut rng = rand::thread_rng(); + + let mut rng = match random_source { + Some(r) => { + let file = File::open(&r[..]).map_err_context(|| { + format!("failed to open random source {}", r.quote()) + })?; + WrappedRng::RngFile(ReadRng::new(file)) + } + None => WrappedRng::RngDefault(rand::thread_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 @@ -501,6 +525,7 @@ fn wipe_file( do_remove(path, path_str, verbose, remove_method) .map_err_context(|| format!("{}: failed to remove file", path.maybe_quote()))?; } + Ok(()) } diff --git a/src/uu/shuf/Cargo.toml b/src/uu/shuf/Cargo.toml index 8b7ae28f8ea..94b7a6c1df1 100644 --- a/src/uu/shuf/Cargo.toml +++ b/src/uu/shuf/Cargo.toml @@ -21,7 +21,7 @@ clap = { workspace = true } memchr = { workspace = true } rand = { workspace = true } rand_core = { workspace = true } -uucore = { workspace = true } +uucore = { workspace = true, features = ["rand-read"] } [[bin]] name = "shuf" diff --git a/src/uu/shuf/src/shuf.rs b/src/uu/shuf/src/shuf.rs index 2d8023448a0..8f39d777b38 100644 --- a/src/uu/shuf/src/shuf.rs +++ b/src/uu/shuf/src/shuf.rs @@ -8,17 +8,16 @@ use clap::{crate_version, Arg, ArgAction, Command}; use memchr::memchr_iter; use rand::prelude::SliceRandom; -use rand::{Rng, RngCore}; +use rand::Rng; use std::collections::HashSet; use std::fs::File; use std::io::{stdin, stdout, BufReader, BufWriter, Error, Read, Write}; use std::ops::RangeInclusive; use uucore::display::Quotable; use uucore::error::{FromIo, UResult, USimpleError, UUsageError}; +use uucore::rand_read::{ReadRng, WrappedRng}; use uucore::{format_usage, help_about, help_usage}; -mod rand_read_adapter; - enum Mode { Default(String), Echo(Vec), @@ -433,7 +432,7 @@ fn shuf_exec(input: &mut impl Shufable, opts: Options) -> UResult<()> { Some(r) => { let file = File::open(&r[..]) .map_err_context(|| format!("failed to open random source {}", r.quote()))?; - WrappedRng::RngFile(rand_read_adapter::ReadRng::new(file)) + WrappedRng::RngFile(ReadRng::new(file)) } None => WrappedRng::RngDefault(rand::thread_rng()), }; @@ -494,41 +493,6 @@ fn parse_head_count(headcounts: Vec) -> Result { Ok(result) } -enum WrappedRng { - RngFile(rand_read_adapter::ReadRng), - RngDefault(rand::rngs::ThreadRng), -} - -impl RngCore for WrappedRng { - fn next_u32(&mut self) -> u32 { - match self { - Self::RngFile(r) => r.next_u32(), - Self::RngDefault(r) => r.next_u32(), - } - } - - fn next_u64(&mut self) -> u64 { - match self { - Self::RngFile(r) => r.next_u64(), - Self::RngDefault(r) => r.next_u64(), - } - } - - fn fill_bytes(&mut self, dest: &mut [u8]) { - match self { - Self::RngFile(r) => r.fill_bytes(dest), - Self::RngDefault(r) => r.fill_bytes(dest), - } - } - - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { - match self { - Self::RngFile(r) => r.try_fill_bytes(dest), - Self::RngDefault(r) => r.try_fill_bytes(dest), - } - } -} - #[cfg(test)] // Since the computed value is a bool, it is more readable to write the expected value out: #[allow(clippy::bool_assert_comparison)] diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 5e1a065a610..92335152f4c 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -26,6 +26,8 @@ dunce = { version = "1.0.4", optional = true } wild = "2.2.1" glob = { workspace = true } lazy_static = "1.4.0" +rand = { workspace = true } +rand_core = { workspace = true } # * optional itertools = { workspace = true, optional = true } thiserror = { workspace = true, optional = true } @@ -92,6 +94,7 @@ pipes = [] process = ["libc"] proc-info = ["tty", "walkdir"] quoting-style = [] +rand-read = [] ranges = [] ringbuffer = [] signals = [] diff --git a/src/uucore/src/lib/features.rs b/src/uucore/src/lib/features.rs index dfe5b773312..86c4032716a 100644 --- a/src/uucore/src/lib/features.rs +++ b/src/uucore/src/lib/features.rs @@ -22,6 +22,8 @@ pub mod fsext; pub mod lines; #[cfg(feature = "quoting-style")] pub mod quoting_style; +#[cfg(feature = "rand-read")] +pub mod rand_read; #[cfg(feature = "ranges")] pub mod ranges; #[cfg(feature = "ringbuffer")] diff --git a/src/uu/shuf/src/rand_read_adapter.rs b/src/uucore/src/lib/features/rand_read.rs similarity index 81% rename from src/uu/shuf/src/rand_read_adapter.rs rename to src/uucore/src/lib/features/rand_read.rs index 728bc0cfbbd..375b971efce 100644 --- a/src/uu/shuf/src/rand_read_adapter.rs +++ b/src/uucore/src/lib/features/rand_read.rs @@ -13,8 +13,8 @@ //! A wrapper around any Read to treat it as an RNG. -use std::fmt; use std::io::Read; +use std::{fmt, fs::File}; use rand_core::{impls, Error, RngCore}; @@ -89,6 +89,41 @@ impl std::error::Error for ReadError { } } +pub enum WrappedRng { + RngFile(ReadRng), + RngDefault(rand::rngs::ThreadRng), +} + +impl RngCore for WrappedRng { + fn next_u32(&mut self) -> u32 { + match self { + Self::RngFile(r) => r.next_u32(), + Self::RngDefault(r) => r.next_u32(), + } + } + + fn next_u64(&mut self) -> u64 { + match self { + Self::RngFile(r) => r.next_u64(), + Self::RngDefault(r) => r.next_u64(), + } + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + match self { + Self::RngFile(r) => r.fill_bytes(dest), + Self::RngDefault(r) => r.fill_bytes(dest), + } + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { + match self { + Self::RngFile(r) => r.try_fill_bytes(dest), + Self::RngDefault(r) => r.try_fill_bytes(dest), + } + } +} + #[cfg(test)] mod test { use std::println; diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index 3200145bd73..024c68b1886 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -54,6 +54,8 @@ pub use crate::features::fs; pub use crate::features::lines; #[cfg(feature = "quoting-style")] pub use crate::features::quoting_style; +#[cfg(feature = "rand-read")] +pub use crate::features::rand_read; #[cfg(feature = "ranges")] pub use crate::features::ranges; #[cfg(feature = "ringbuffer")]