@@ -10,7 +10,7 @@ use clap::{Arg, ArgAction, Command};
1010use libc:: S_IWUSR ;
1111use rand:: { Rng , SeedableRng , rngs:: StdRng , seq:: SliceRandom } ;
1212use std:: fs:: { self , File , OpenOptions } ;
13- use std:: io:: { self , Seek , Write } ;
13+ use std:: io:: { self , Read , Seek , Write } ;
1414#[ cfg( unix) ]
1515use std:: os:: unix:: prelude:: PermissionsExt ;
1616use std:: path:: { Path , PathBuf } ;
@@ -34,6 +34,7 @@ pub mod options {
3434 pub const VERBOSE : & str = "verbose" ;
3535 pub const EXACT : & str = "exact" ;
3636 pub const ZERO : & str = "zero" ;
37+ pub const RANDOM_SOURCE : & str = "random-source" ;
3738
3839 pub mod remove {
3940 pub const UNLINK : & str = "unlink" ;
@@ -152,16 +153,25 @@ impl Iterator for FilenameIter {
152153 }
153154}
154155
156+ enum RandomSource {
157+ System ,
158+ Read ( File ) ,
159+ }
160+
155161/// Used to generate blocks of bytes of size <= BLOCK_SIZE based on either a give pattern
156162/// or randomness
157163// The lint warns about a large difference because StdRng is big, but the buffers are much
158164// larger anyway, so it's fine.
159165#[ allow( clippy:: large_enum_variant) ]
160- enum BytesWriter {
166+ enum BytesWriter < ' a > {
161167 Random {
162168 rng : StdRng ,
163169 buffer : [ u8 ; BLOCK_SIZE ] ,
164170 } ,
171+ RandomFile {
172+ rng_file : & ' a File ,
173+ buffer : [ u8 ; BLOCK_SIZE ] ,
174+ } ,
165175 // To write patterns we only write to the buffer once. To be able to do
166176 // this, we need to extend the buffer with 2 bytes. We can then easily
167177 // obtain a buffer starting with any character of the pattern that we
@@ -177,12 +187,18 @@ enum BytesWriter {
177187 } ,
178188}
179189
180- impl BytesWriter {
181- fn from_pass_type ( pass : & PassType ) -> Self {
190+ impl < ' a > BytesWriter < ' a > {
191+ fn from_pass_type ( pass : & PassType , random_source : & ' a RandomSource ) -> Self {
182192 match pass {
183- PassType :: Random => Self :: Random {
184- rng : StdRng :: from_os_rng ( ) ,
185- buffer : [ 0 ; BLOCK_SIZE ] ,
193+ PassType :: Random => match random_source {
194+ RandomSource :: System => Self :: Random {
195+ rng : StdRng :: from_os_rng ( ) ,
196+ buffer : [ 0 ; BLOCK_SIZE ] ,
197+ } ,
198+ RandomSource :: Read ( file) => Self :: RandomFile {
199+ rng_file : file,
200+ buffer : [ 0 ; BLOCK_SIZE ] ,
201+ } ,
186202 } ,
187203 PassType :: Pattern ( pattern) => {
188204 // Copy the pattern in chunks rather than simply one byte at a time
@@ -203,17 +219,22 @@ impl BytesWriter {
203219 }
204220 }
205221
206- fn bytes_for_pass ( & mut self , size : usize ) -> & [ u8 ] {
222+ fn bytes_for_pass ( & mut self , size : usize ) -> Result < & [ u8 ] , io :: Error > {
207223 match self {
208224 Self :: Random { rng, buffer } => {
209225 let bytes = & mut buffer[ ..size] ;
210226 rng. fill ( bytes) ;
211- bytes
227+ Ok ( bytes)
228+ }
229+ Self :: RandomFile { rng_file, buffer } => {
230+ let bytes = & mut buffer[ ..size] ;
231+ rng_file. read_exact ( bytes) ?;
232+ Ok ( bytes)
212233 }
213234 Self :: Pattern { offset, buffer } => {
214235 let bytes = & buffer[ * offset..size + * offset] ;
215236 * offset = ( * offset + size) % PATTERN_LENGTH ;
216- bytes
237+ Ok ( bytes)
217238 }
218239 }
219240 }
@@ -240,6 +261,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
240261 None => unreachable ! ( ) ,
241262 } ;
242263
264+ let random_source = match matches. get_one :: < String > ( options:: RANDOM_SOURCE ) {
265+ Some ( filepath) => RandomSource :: Read ( File :: open ( filepath) . map_err ( |_| {
266+ USimpleError :: new (
267+ 1 ,
268+ format ! ( "cannot open random source: {}" , filepath. quote( ) ) ,
269+ )
270+ } ) ?) ,
271+ None => RandomSource :: System ,
272+ } ;
243273 // TODO: implement --random-source
244274
245275 let remove_method = if matches. get_flag ( options:: WIPESYNC ) {
@@ -275,6 +305,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
275305 size,
276306 exact,
277307 zero,
308+ & random_source,
278309 verbose,
279310 force,
280311 ) ) ;
@@ -356,6 +387,13 @@ pub fn uu_app() -> Command {
356387 . help ( "add a final overwrite with zeros to hide shredding" )
357388 . action ( ArgAction :: SetTrue ) ,
358389 )
390+ . arg (
391+ Arg :: new ( options:: RANDOM_SOURCE )
392+ . long ( options:: RANDOM_SOURCE )
393+ . help ( "take random bytes from FILE" )
394+ . value_hint ( clap:: ValueHint :: FilePath )
395+ . action ( ArgAction :: Set ) ,
396+ )
359397 // Positional arguments
360398 . arg (
361399 Arg :: new ( options:: FILE )
@@ -395,6 +433,7 @@ fn wipe_file(
395433 size : Option < u64 > ,
396434 exact : bool ,
397435 zero : bool ,
436+ random_source : & RandomSource ,
398437 verbose : bool ,
399438 force : bool ,
400439) -> UResult < ( ) > {
@@ -501,7 +540,7 @@ fn wipe_file(
501540 // size is an optional argument for exactly how many bytes we want to shred
502541 // Ignore failed writes; just keep trying
503542 show_if_err ! (
504- do_pass( & mut file, & pass_type, exact, size)
543+ do_pass( & mut file, & pass_type, exact, random_source , size)
505544 . map_err_context( || format!( "{}: File write pass failed" , path. maybe_quote( ) ) )
506545 ) ;
507546 }
@@ -534,22 +573,23 @@ fn do_pass(
534573 file : & mut File ,
535574 pass_type : & PassType ,
536575 exact : bool ,
576+ random_source : & RandomSource ,
537577 file_size : u64 ,
538578) -> Result < ( ) , io:: Error > {
539579 // We might be at the end of the file due to a previous iteration, so rewind.
540580 file. rewind ( ) ?;
541581
542- let mut writer = BytesWriter :: from_pass_type ( pass_type) ;
582+ let mut writer = BytesWriter :: from_pass_type ( pass_type, random_source ) ;
543583 let ( number_of_blocks, bytes_left) = split_on_blocks ( file_size, exact) ;
544584
545585 // We start by writing BLOCK_SIZE times as many time as possible.
546586 for _ in 0 ..number_of_blocks {
547- let block = writer. bytes_for_pass ( BLOCK_SIZE ) ;
587+ let block = writer. bytes_for_pass ( BLOCK_SIZE ) ? ;
548588 file. write_all ( block) ?;
549589 }
550590
551591 // Then we write remaining data which is smaller than the BLOCK_SIZE
552- let block = writer. bytes_for_pass ( bytes_left as usize ) ;
592+ let block = writer. bytes_for_pass ( bytes_left as usize ) ? ;
553593 file. write_all ( block) ?;
554594
555595 file. sync_data ( ) ?;
0 commit comments